Merge "Support Kotlin concrete functions in DAO interfaces." into androidx-master-dev
diff --git a/animation/animation/src/main/java/androidx/animation/AnimationHandler.java b/animation/animation/src/main/java/androidx/animation/AnimationHandler.java
index 2e4241b..c6f1dc6 100644
--- a/animation/animation/src/main/java/androidx/animation/AnimationHandler.java
+++ b/animation/animation/src/main/java/androidx/animation/AnimationHandler.java
@@ -73,7 +73,8 @@
 
     public static AnimationHandler sAnimationHandler = null;
     private static AnimationHandler sTestHandler = null;
-    private ThreadLocal<AnimationCallbackData> mAnimationCallbackData = new ThreadLocal<>();
+    private static final ThreadLocal<AnimationCallbackData> mAnimationCallbackData =
+            new ThreadLocal<>();
     private final AnimationFrameCallbackProvider mProvider;
 
     AnimationHandler(AnimationFrameCallbackProvider provider) {
@@ -81,7 +82,7 @@
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                 mProvider = new FrameCallbackProvider16();
             } else {
-                mProvider = new FrameCallbackProvider14();
+                mProvider = new FrameCallbackProvider14(this);
             }
         } else {
             mProvider = provider;
@@ -291,13 +292,16 @@
      * Frame provider for ICS and ICS-MR1 releases. The frame callback is achieved via posting
      * a Runnable to the main thread Handler with a delay.
      */
-    private class FrameCallbackProvider14 implements AnimationFrameCallbackProvider, Runnable {
+    private static class FrameCallbackProvider14 implements AnimationFrameCallbackProvider,
+            Runnable {
 
-        private final ThreadLocal<Handler> mHandler = new ThreadLocal<>();
+        private static final ThreadLocal<Handler> mHandler = new ThreadLocal<>();
         private long mLastFrameTime = -1;
         private long mFrameDelay = 16;
+        AnimationHandler mAnimationHandler;
 
-        FrameCallbackProvider14() {
+        FrameCallbackProvider14(AnimationHandler animationHandler) {
+            mAnimationHandler = animationHandler;
         }
 
         Handler getHandler() {
@@ -310,7 +314,7 @@
         @Override
         public void run() {
             mLastFrameTime = SystemClock.uptimeMillis();
-            AnimationHandler.this.onAnimationFrame(mLastFrameTime);
+            mAnimationHandler.onAnimationFrame(mLastFrameTime);
         }
 
         @Override
diff --git a/appcompat/appcompat-lint/build.gradle b/appcompat/appcompat-lint/build.gradle
index d67f346..8159beb 100644
--- a/appcompat/appcompat-lint/build.gradle
+++ b/appcompat/appcompat-lint/build.gradle
@@ -14,15 +14,11 @@
  * limitations under the License.
  */
 
-import androidx.build.AndroidXExtension
-import androidx.build.CompilationTarget
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
 import androidx.build.Publish
 
-import static androidx.build.dependencies.DependenciesKt.LINT_API_MIN
-import static androidx.build.dependencies.DependenciesKt.getKOTLIN_STDLIB
-import static androidx.build.dependencies.DependenciesKt.getLINT_API_LATEST
+import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("AndroidXPlugin")
diff --git a/benchmark/gradle-plugin/build.gradle b/benchmark/gradle-plugin/build.gradle
index c794a45..43ebd26 100644
--- a/benchmark/gradle-plugin/build.gradle
+++ b/benchmark/gradle-plugin/build.gradle
@@ -38,6 +38,7 @@
     implementation(KOTLIN_STDLIB)
 
     testImplementation gradleTestKit()
+    testImplementation(project(":internal-testutils-gradle-plugin"))
     testImplementation(ANDROIDX_TEST_RUNNER)
     testImplementation(JUNIT)
     testImplementation(KOTLIN_TEST)
diff --git a/benchmark/gradle-plugin/src/test/kotlin/androidx/benchmark/gradle/BenchmarkPluginTest.kt b/benchmark/gradle-plugin/src/test/kotlin/androidx/benchmark/gradle/BenchmarkPluginTest.kt
index 7844207..5d04073 100644
--- a/benchmark/gradle-plugin/src/test/kotlin/androidx/benchmark/gradle/BenchmarkPluginTest.kt
+++ b/benchmark/gradle-plugin/src/test/kotlin/androidx/benchmark/gradle/BenchmarkPluginTest.kt
@@ -16,95 +16,56 @@
 
 package androidx.benchmark.gradle
 
+import androidx.testutils.gradle.ProjectSetupRule
 import org.gradle.testkit.runner.GradleRunner
 import org.gradle.testkit.runner.UnexpectedBuildFailure
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
-import org.junit.rules.TemporaryFolder
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 import java.io.File
-import java.util.Properties
 import kotlin.test.assertFailsWith
 import kotlin.test.assertFalse
 import kotlin.test.assertTrue
 
+private val PLUGINS_HEADER = """
+    plugins {
+        id('androidx.benchmark')
+        id('com.android.library')
+    }
+""".trimIndent()
+
 @RunWith(JUnit4::class)
 class BenchmarkPluginTest {
-
     @get:Rule
-    val testProjectDir = TemporaryFolder()
+    val projectSetup = ProjectSetupRule()
 
-    private lateinit var buildToolsVersion: String
-    private lateinit var compileSdkVersion: String
-    private lateinit var prebuiltsRoot: String
-    private lateinit var minSdkVersion: String
-
-    private lateinit var buildFile: File
-    private lateinit var propertiesFile: File
     private lateinit var versionPropertiesFile: File
     private lateinit var gradleRunner: GradleRunner
 
     @Before
     fun setUp() {
-        val stream = BenchmarkPluginTest::class.java.classLoader.getResourceAsStream("sdk.prop")
-        val properties = Properties()
-        properties.load(stream)
-        prebuiltsRoot = properties.getProperty("prebuiltsRoot")
-        compileSdkVersion = properties.getProperty("compileSdkVersion")
-        buildToolsVersion = properties.getProperty("buildToolsVersion")
-        minSdkVersion = properties.getProperty("minSdkVersion")
-
-        testProjectDir.root.mkdirs()
-
-        val localPropFile = File("../../local.properties")
-        localPropFile.copyTo(File(testProjectDir.root, "local.properties"), overwrite = true)
-
-        buildFile = File(testProjectDir.root, "build.gradle")
-        buildFile.createNewFile()
-
-        propertiesFile = File(testProjectDir.root, "gradle.properties")
-        propertiesFile.writer().use {
-            val props = Properties()
-            props.setProperty("android.useAndroidX", "true")
-            props.setProperty("android.enableJetpack", "true")
-            props.store(it, null)
-        }
-
-        versionPropertiesFile = File(testProjectDir.root, "version.properties")
+        versionPropertiesFile = File(projectSetup.rootDir, "version.properties")
         versionPropertiesFile.createNewFile()
 
-        File("src/test/test-data", "app-project").copyRecursively(testProjectDir.root)
+        File("src/test/test-data", "app-project").copyRecursively(projectSetup.rootDir)
 
         gradleRunner = GradleRunner.create()
-            .withProjectDir(testProjectDir.root)
+            .withProjectDir(projectSetup.rootDir)
             .withPluginClasspath()
     }
 
     @Test
     fun applyPluginAppProject() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('com.android.application')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
+        projectSetup.writeDefaultBuildGradle(
+            prefix = """
+                plugins {
+                    id('com.android.application')
+                    id('androidx.benchmark')
                 }
-            }
-
+            """.trimIndent(),
+            suffix = """
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
             }
@@ -118,27 +79,14 @@
 
     @Test
     fun applyPluginAndroidLibProject() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('com.android.library')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
+        projectSetup.writeDefaultBuildGradle(
+            prefix = """
+                plugins {
+                    id('com.android.library')
+                    id('androidx.benchmark')
                 }
-            }
-
+            """.trimIndent(),
+            suffix = """
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
             }
@@ -152,9 +100,11 @@
 
     @Test
     fun applyPluginNonAndroidProject() {
-        buildFile.writeText(
+        val prebuiltsRoot = projectSetup.props.prebuiltsRoot
+        projectSetup.buildFile.writeText(
             """
             plugins {
+                id('java')
                 id('androidx.benchmark')
             }
 
@@ -163,49 +113,22 @@
                 maven { url "$prebuiltsRoot/androidx/internal" }
             }
 
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
-                }
-            }
-
             dependencies {
-                androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
+                testImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
             }
         """.trimIndent()
         )
 
         assertFailsWith(UnexpectedBuildFailure::class) {
-            gradleRunner.withArguments("assemble").build()
+            gradleRunner.withArguments("jar").build()
         }
     }
 
     @Test
     fun applyPluginNonBenchmarkProject() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('com.android.library')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
-                }
-            }
-        """.trimIndent()
+        projectSetup.writeDefaultBuildGradle(
+            prefix = PLUGINS_HEADER,
+            suffix = ""
         )
 
         val output = gradleRunner.withArguments("tasks").build()
@@ -215,27 +138,9 @@
 
     @Test
     fun applyPluginBeforeAndroid() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('androidx.benchmark')
-                id('com.android.library')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
-                }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = PLUGINS_HEADER,
+            suffix = """
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
             }
@@ -249,24 +154,11 @@
 
     @Test
     fun applyPluginOnAgp36() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('androidx.benchmark')
-                id('com.android.library')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = PLUGINS_HEADER,
+            suffix = """
             android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
                 defaultConfig {
-                    minSdkVersion $minSdkVersion
                     testInstrumentationRunnerArguments additionalTestOutputDir: "/fake_path/files"
                 }
             }
@@ -281,7 +173,7 @@
         """.trimIndent()
         )
 
-        propertiesFile.appendText("android.enableAdditionalTestOutput=true")
+        projectSetup.gradlePropertiesFile.appendText("android.enableAdditionalTestOutput=true")
         versionPropertiesFile.writeText("buildVersion=3.6.0-alpha05")
 
         val output = gradleRunner.withArguments("tasks").build()
@@ -297,31 +189,17 @@
 
     @Test
     fun applyPluginOnAgp35() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('androidx.benchmark')
-                id('com.android.library')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = PLUGINS_HEADER,
+            suffix = """
             android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
                 defaultConfig {
-                    minSdkVersion $minSdkVersion
                     testInstrumentationRunnerArguments.remove("additionalTestOutputDir")
                 }
             }
 
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
-
             }
 
             tasks.register("printInstrumentationArgs") {
@@ -331,7 +209,7 @@
             tasks.register("printTestBuildType") {
                 println android.testBuildType
             }
-        """.trimIndent()
+            """.trimIndent()
         )
 
         versionPropertiesFile.writeText("buildVersion=3.5.0-rc03")
@@ -352,32 +230,11 @@
 
     @Test
     fun applyPluginDefaultAgpProperties() {
-        buildFile.writeText(
-            """
-            import com.android.build.gradle.TestedExtension
-
-            plugins {
-                id('com.android.library')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
-                }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = "import com.android.build.gradle.TestedExtension\n$PLUGINS_HEADER",
+            suffix = """
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
-
             }
 
             tasks.register("printTestInstrumentationRunner") {
@@ -388,7 +245,7 @@
                 def extension = project.extensions.getByType(TestedExtension)
                 println extension.buildTypes.getByName("debug").testCoverageEnabled
             }
-        """.trimIndent()
+            """.trimIndent()
         )
 
         val runnerOutput = gradleRunner.withArguments("printTestInstrumentationRunner").build()
@@ -402,26 +259,11 @@
 
     @Test
     fun applyPluginOverrideAgpProperties() {
-        buildFile.writeText(
-            """
-            import com.android.build.gradle.TestedExtension
-
-            plugins {
-                id('com.android.library')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = "import com.android.build.gradle.TestedExtension\n$PLUGINS_HEADER",
+            suffix = """
             android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
                 defaultConfig {
-                    minSdkVersion $minSdkVersion
                     testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
                 }
 
@@ -434,7 +276,6 @@
 
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
-
             }
 
             tasks.register("printTestInstrumentationRunner") {
@@ -459,24 +300,11 @@
 
     @Test
     fun applyPluginAndroidOldRunner36() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('com.android.library')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = PLUGINS_HEADER,
+            suffix = """
             android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
                 defaultConfig {
-                    minSdkVersion $minSdkVersion
                     testInstrumentationRunner "androidx.benchmark.AndroidBenchmarkRunner"
                     testInstrumentationRunnerArguments additionalTestOutputDir: "/fake_path/files"
                 }
@@ -485,10 +313,9 @@
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha04"
             }
-        """.trimIndent()
+            """.trimIndent()
         )
-
-        propertiesFile.appendText("android.enableAdditionalTestOutput=true")
+        projectSetup.gradlePropertiesFile.appendText("android.enableAdditionalTestOutput=true")
 
         assertFailsWith(UnexpectedBuildFailure::class) {
             gradleRunner.withArguments("assemble").build()
@@ -497,24 +324,11 @@
 
     @Test
     fun applyPluginAndroidOldRunner35() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('com.android.library')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = PLUGINS_HEADER,
+            suffix = """
             android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
                 defaultConfig {
-                    minSdkVersion $minSdkVersion
                     testInstrumentationRunner "androidx.benchmark.AndroidBenchmarkRunner"
                     testInstrumentationRunnerArguments.remove("additionalTestOutputDir")
                 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
index 307500d..af794f6 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
@@ -255,6 +255,7 @@
     private fun Project.configureRootProject() {
         setDependencyVersions()
         configureKtlintCheckFile()
+        configureCheckInvalidSuppress()
 
         if (isRunningOnBuildServer()) {
             gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS
diff --git a/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt b/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
index 274eed9..d87c862 100644
--- a/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
@@ -101,8 +101,14 @@
             "-Xep:EqualsGetClass:ERROR",
             "-Xep:UnusedVariable:ERROR",
             "-Xep:UnusedMethod:ERROR",
+            "-Xep:UndefinedEquals:ERROR",
+            "-Xep:ThreadLocalUsage:ERROR",
             "-Xep:FutureReturnValueIgnored:ERROR",
+            "-Xep:ArgumentSelectionDefectChecker:ERROR",
             "-Xep:HidingField:ERROR",
+            "-Xep:UnsynchronizedOverridesSynchronized:ERROR",
+            "-Xep:Finally:ERROR",
+            "-Xep:ThreadPriorityCheck:ERROR",
             "-Xep:AutoValueFinalMethods:ERROR",
 
             // Nullaway
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
index 081337c..5030956 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
@@ -34,7 +34,6 @@
     val BENCHMARK = LibraryGroup("androidx.benchmark")
     val CAMERA = LibraryGroup("androidx.camera", false)
     val CAR = LibraryGroup("androidx.car", false)
-    val CAR_APP = LibraryGroup("androidx.car.app")
     val CARDVIEW = LibraryGroup("androidx.cardview")
     val COLLECTION = LibraryGroup("androidx.collection")
     val CONCURRENT = LibraryGroup("androidx.concurrent")
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index fd90bb1..5592f5e 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -41,7 +41,6 @@
     val CAMERA_LIFECYCLE = Version("1.0.0-alpha02")
     val CAMERA_VIEW = Version("1.0.0-alpha05")
     val CAR = Version("1.0.0-alpha8")
-    val CAR_APP = Version("1.0.0-alpha01")
     val CAR_MODERATOR = Version("1.0.0-alpha1")
     val CARDVIEW = Version("1.1.0-alpha01")
     val COLLECTION = Version("1.2.0-alpha01")
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index 3ea3629..c0c5777 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -41,7 +41,7 @@
     ignore(LibraryGroups.BENCHMARK.group, "benchmark-gradle-plugin")
     prebuilts(LibraryGroups.BENCHMARK, "1.0.0")
     prebuilts(LibraryGroups.BIOMETRIC, "biometric", "1.0.1")
-    prebuilts(LibraryGroups.BROWSER, "1.2.0")
+    prebuilts(LibraryGroups.BROWSER, "1.3.0-alpha01")
     ignore(LibraryGroups.CAMERA.group, "camera-testing")
     ignore(LibraryGroups.CAMERA.group, "camera-extensions-stub")
     ignore(LibraryGroups.CAMERA.group, "camera-testlib-extensions")
@@ -73,7 +73,7 @@
     ignore(LibraryGroups.FRAGMENT.group, "fragment-lint")
     ignore(LibraryGroups.FRAGMENT.group, "fragment-testing-lint")
     ignore(LibraryGroups.FRAGMENT.group, "fragment-truth")
-    prebuilts(LibraryGroups.FRAGMENT, "1.2.0-rc04")
+    prebuilts(LibraryGroups.FRAGMENT, "1.2.0-rc05")
     prebuilts(LibraryGroups.GRIDLAYOUT, "1.0.0")
     prebuilts(LibraryGroups.HEIFWRITER, "1.0.0")
     prebuilts(LibraryGroups.INTERPOLATOR, "1.0.0")
@@ -102,6 +102,7 @@
     ignore(LibraryGroups.NAVIGATION.group, "navigation-safe-args-gradle-plugin")
     prebuilts(LibraryGroups.NAVIGATION, "2.2.0-rc04")
     ignore(LibraryGroups.PAGING.group, "paging-guava")
+    ignore(LibraryGroups.PAGING.group, "paging-testutils")
     prebuilts(LibraryGroups.PAGING, "2.1.0")
     prebuilts(LibraryGroups.PALETTE, "1.0.0")
     prebuilts(LibraryGroups.PERCENTLAYOUT, "1.0.0")
@@ -143,7 +144,7 @@
     prebuilts(LibraryGroups.WEBKIT, "1.2.0-alpha01")
     ignore(LibraryGroups.WORK.group, "work-gcm")
     ignore(LibraryGroups.WORK.group, "work-runtime-lint")
-    prebuilts(LibraryGroups.WORK, "2.3.0-beta02")
+    prebuilts(LibraryGroups.WORK, "2.3.0-rc01")
     default(Ignore)
 }
 
diff --git a/buildSrc/src/main/kotlin/androidx/build/SdkResourceGenerator.kt b/buildSrc/src/main/kotlin/androidx/build/SdkResourceGenerator.kt
index 5defbc6..c2ebd63 100644
--- a/buildSrc/src/main/kotlin/androidx/build/SdkResourceGenerator.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SdkResourceGenerator.kt
@@ -59,6 +59,9 @@
     val kotlinStdlib: String = KOTLIN_STDLIB
 
     @get:Input
+    val rootProjectPath: String = project.rootProject.rootDir.absolutePath
+
+    @get:Input
     lateinit var gradleVersion: String
 
     @get:OutputFile
@@ -77,6 +80,7 @@
         writer.write("navigationCommon=$navigationCommon\n")
         writer.write("kotlinStdlib=$kotlinStdlib\n")
         writer.write("gradleVersion=$gradleVersion\n")
+        writer.write("rootProjectPath=$rootProjectPath\n")
         writer.close()
     }
 
diff --git a/buildSrc/src/main/kotlin/androidx/build/checkInvalidSuppress.kt b/buildSrc/src/main/kotlin/androidx/build/checkInvalidSuppress.kt
new file mode 100644
index 0000000..85033cd
--- /dev/null
+++ b/buildSrc/src/main/kotlin/androidx/build/checkInvalidSuppress.kt
@@ -0,0 +1,120 @@
+/*
+ * 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.build
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.GradleException
+import org.gradle.api.Project
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.TaskAction
+import org.gradle.api.tasks.options.Option
+import org.jetbrains.kotlin.konan.file.File
+
+// first level line filter - we ignore lines that don't contain the following for speed
+const val ROOT_MATCH = "noinspection"
+// Map of invalid pattern to correct replacement
+// These could be regex for fancy whitespace matching, but don't seem to need in practice
+val MATCHERS = mapOf(
+    "//noinspection deprecation" to "@SuppressWarnings(\"deprecation\")",
+    "//noinspection unchecked" to "@SuppressWarnings(\"unchecked\")"
+)
+
+open class CheckInvalidSuppressTask : DefaultTask() {
+
+    @Option(option = "files", description = "List of files to check")
+    @Internal
+    lateinit var filenamesOption: String
+
+    @Option(
+        option = "format",
+        description = "Use --format to auto-correct invalid suppressions"
+    )
+    @Internal
+    var autocorrectOption = false
+
+    // Check a line for any invalid suppressions, and return it if found
+    fun getInvalidSuppression(line: String): String {
+        MATCHERS.keys.forEach { bad ->
+            if (line.trimStart().startsWith(bad)) {
+                return bad
+            }
+        }
+        return ""
+    }
+
+    // Get report for line with invalid suppression
+    fun getReportForLine(filename: String, i: Int, lines: List<String>, good: String): String {
+        var context = ""
+        for (index in 0..2) {
+            if (index + i >= lines.size) break
+            context += lines[index + i] + "\n"
+        }
+        return "\n%s:%d:\nError: unsupported comment suppression\n%sInstead, use: %s\n"
+            .format(
+                filename, i, context, good
+            )
+    }
+
+    @TaskAction
+    fun checkForInvalidSuppression() {
+        val filenames = filenamesOption.split(" ")
+        var report = ""
+
+        for (filename in filenames) {
+            // suppress comments ignored in kotlin, but may as well block there too
+            if (!filename.endsWith(".java") && !filename.endsWith(".kt"))
+                continue
+
+            // check file exists
+            if (!File(filename).exists)
+                continue
+
+            val lines = File(filename).readStrings()
+            for ((i, line) in lines.withIndex()) {
+                if (line.contains(ROOT_MATCH)) {
+                    val bad = getInvalidSuppression(line)
+                    if (bad != "") {
+                        if (autocorrectOption) {
+                            if (line.trimStart() == bad) {
+                                lines[i] = line.replaceFirst(bad, MATCHERS[bad]!!)
+                            } else {
+                                lines[i] = line.replaceFirst(bad, MATCHERS[bad]!! + " // ")
+                            }
+                            File(filename).writeLines(lines)
+                        } else {
+                            report += getReportForLine(filename, i, lines, MATCHERS[bad]!!)
+                        }
+                    }
+                }
+            }
+        }
+        if (report != "") {
+            throw GradleException(
+                "Invalid, IDEA-specific warning suppression found. These cause " +
+                        "warnings during compilation." + "\n" + report
+            )
+        }
+    }
+}
+
+fun Project.configureCheckInvalidSuppress() {
+    tasks.register("checkInvalidSuppress", CheckInvalidSuppressTask::class.java) { task ->
+        task.description = "Task used to find IDEA-specific warning suppressions that do not " +
+                "work for command line builds."
+        task.group = "Verification"
+    }
+}
diff --git a/camera/camera-camera2/api/1.0.0-alpha08.txt b/camera/camera-camera2/api/1.0.0-alpha08.txt
new file mode 100644
index 0000000..c4e0698
--- /dev/null
+++ b/camera/camera-camera2/api/1.0.0-alpha08.txt
@@ -0,0 +1,9 @@
+// Signature format: 3.0
+package androidx.camera.camera2 {
+
+  public final class Camera2Config {
+    method public static androidx.camera.core.CameraXConfig defaultConfig();
+  }
+
+}
+
diff --git a/camera/camera-camera2/api/current.txt b/camera/camera-camera2/api/current.txt
new file mode 100644
index 0000000..c4e0698
--- /dev/null
+++ b/camera/camera-camera2/api/current.txt
@@ -0,0 +1,9 @@
+// Signature format: 3.0
+package androidx.camera.camera2 {
+
+  public final class Camera2Config {
+    method public static androidx.camera.core.CameraXConfig defaultConfig();
+  }
+
+}
+
diff --git a/camera/camera-camera2/api/public_plus_experimental_1.0.0-alpha08.txt b/camera/camera-camera2/api/public_plus_experimental_1.0.0-alpha08.txt
new file mode 100644
index 0000000..a6359b6
--- /dev/null
+++ b/camera/camera-camera2/api/public_plus_experimental_1.0.0-alpha08.txt
@@ -0,0 +1,31 @@
+// Signature format: 3.0
+package androidx.camera.camera2 {
+
+  public final class Camera2Config {
+    method public static androidx.camera.core.CameraXConfig defaultConfig();
+  }
+
+}
+
+package androidx.camera.camera2.interop {
+
+  @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2CameraInfo {
+    method public static String extractCameraId(androidx.camera.core.CameraInfo);
+  }
+
+  @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2Interop {
+  }
+
+  public static final class Camera2Interop.Extender<T> {
+    ctor public Camera2Interop.Extender(androidx.camera.core.ExtendableBuilder<T!>);
+    method public <ValueT> androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>, ValueT);
+    method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setDeviceStateCallback(android.hardware.camera2.CameraDevice.StateCallback);
+    method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setSessionCaptureCallback(android.hardware.camera2.CameraCaptureSession.CaptureCallback);
+    method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setSessionStateCallback(android.hardware.camera2.CameraCaptureSession.StateCallback);
+  }
+
+  @experimental.Experimental @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalCamera2Interop {
+  }
+
+}
+
diff --git a/camera/camera-camera2/api/public_plus_experimental_current.txt b/camera/camera-camera2/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..a6359b6
--- /dev/null
+++ b/camera/camera-camera2/api/public_plus_experimental_current.txt
@@ -0,0 +1,31 @@
+// Signature format: 3.0
+package androidx.camera.camera2 {
+
+  public final class Camera2Config {
+    method public static androidx.camera.core.CameraXConfig defaultConfig();
+  }
+
+}
+
+package androidx.camera.camera2.interop {
+
+  @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2CameraInfo {
+    method public static String extractCameraId(androidx.camera.core.CameraInfo);
+  }
+
+  @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2Interop {
+  }
+
+  public static final class Camera2Interop.Extender<T> {
+    ctor public Camera2Interop.Extender(androidx.camera.core.ExtendableBuilder<T!>);
+    method public <ValueT> androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>, ValueT);
+    method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setDeviceStateCallback(android.hardware.camera2.CameraDevice.StateCallback);
+    method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setSessionCaptureCallback(android.hardware.camera2.CameraCaptureSession.CaptureCallback);
+    method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setSessionStateCallback(android.hardware.camera2.CameraCaptureSession.StateCallback);
+  }
+
+  @experimental.Experimental @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalCamera2Interop {
+  }
+
+}
+
diff --git a/car-app/app/api/res-1.0.0-alpha01.txt b/camera/camera-camera2/api/res-1.0.0-alpha08.txt
similarity index 100%
rename from car-app/app/api/res-1.0.0-alpha01.txt
rename to camera/camera-camera2/api/res-1.0.0-alpha08.txt
diff --git a/camera/camera-camera2/api/restricted_1.0.0-alpha08.txt b/camera/camera-camera2/api/restricted_1.0.0-alpha08.txt
new file mode 100644
index 0000000..c4e0698
--- /dev/null
+++ b/camera/camera-camera2/api/restricted_1.0.0-alpha08.txt
@@ -0,0 +1,9 @@
+// Signature format: 3.0
+package androidx.camera.camera2 {
+
+  public final class Camera2Config {
+    method public static androidx.camera.core.CameraXConfig defaultConfig();
+  }
+
+}
+
diff --git a/camera/camera-camera2/api/restricted_current.txt b/camera/camera-camera2/api/restricted_current.txt
new file mode 100644
index 0000000..c4e0698
--- /dev/null
+++ b/camera/camera-camera2/api/restricted_current.txt
@@ -0,0 +1,9 @@
+// Signature format: 3.0
+package androidx.camera.camera2 {
+
+  public final class Camera2Config {
+    method public static androidx.camera.core.CameraXConfig defaultConfig();
+  }
+
+}
+
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraControlDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraControlDeviceTest.java
index a621c5a..905162d 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraControlDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraControlDeviceTest.java
@@ -153,7 +153,7 @@
         assumeTrue(isSupportAeRegion(mCameraSelector));
 
         FocusMeteringAction action =
-                FocusMeteringAction.Builder.from(mMeteringPoint1,
+                new FocusMeteringAction.Builder(mMeteringPoint1,
                         FocusMeteringAction.FLAG_AE).build();
         ListenableFuture<FocusMeteringResult> future =
                 mCamera.getCameraControl().startFocusAndMetering(action);
@@ -166,7 +166,7 @@
         assumeTrue(isSupportAwbRegion(mCameraSelector));
 
         FocusMeteringAction action =
-                FocusMeteringAction.Builder.from(mMeteringPoint1,
+                new FocusMeteringAction.Builder(mMeteringPoint1,
                         FocusMeteringAction.FLAG_AWB).build();
         ListenableFuture<FocusMeteringResult> future =
                 mCamera.getCameraControl().startFocusAndMetering(action);
@@ -179,7 +179,7 @@
         assumeTrue(isSupportAeRegion(mCameraSelector) || isSupportAwbRegion(mCameraSelector));
 
         FocusMeteringAction action =
-                FocusMeteringAction.Builder.from(mMeteringPoint1,
+                new FocusMeteringAction.Builder(mMeteringPoint1,
                         FocusMeteringAction.FLAG_AE | FocusMeteringAction.FLAG_AWB).build();
         ListenableFuture<FocusMeteringResult> future =
                 mCamera.getCameraControl().startFocusAndMetering(action);
@@ -198,7 +198,7 @@
         MeteringPoint point3 = factory.createPoint(0.2f, 0.2f);
         MeteringPoint point4 = factory.createPoint(0.3f, 0.4f);
         FocusMeteringAction action =
-                FocusMeteringAction.Builder.from(point1,
+                new FocusMeteringAction.Builder(point1,
                         FocusMeteringAction.FLAG_AE | FocusMeteringAction.FLAG_AWB)
                         .addPoint(point2,
                                 FocusMeteringAction.FLAG_AE | FocusMeteringAction.FLAG_AWB)
@@ -217,7 +217,7 @@
     @Test
     public void cancelFocusMetering_futureCompletes() {
         FocusMeteringAction action =
-                FocusMeteringAction.Builder.from(mMeteringPoint1).build();
+                new FocusMeteringAction.Builder(mMeteringPoint1).build();
         mCamera.getCameraControl().startFocusAndMetering(action);
         ListenableFuture<Void> result = mCamera.getCameraControl().cancelFocusAndMetering();
 
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlTest.java
index 6ee4db5..ce5f92c 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlTest.java
@@ -408,7 +408,7 @@
 
         SurfaceOrientedMeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(1.0f,
                 1.0f);
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0))
+        FocusMeteringAction action = new FocusMeteringAction.Builder(factory.createPoint(0, 0))
                 .build();
         mCamera2CameraControl.startFocusAndMetering(action);
 
@@ -448,7 +448,7 @@
 
         SurfaceOrientedMeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(1.0f,
                 1.0f);
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0))
+        FocusMeteringAction action = new FocusMeteringAction.Builder(factory.createPoint(0, 0))
                 .build();
         mCamera2CameraControl.startFocusAndMetering(action);
         HandlerUtil.waitForLooperToIdle(mHandler);
@@ -474,7 +474,7 @@
 
         SurfaceOrientedMeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(1.0f,
                 1.0f);
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0),
+        FocusMeteringAction action = new FocusMeteringAction.Builder(factory.createPoint(0, 0),
                 FocusMeteringAction.FLAG_AE | FocusMeteringAction.FLAG_AWB)
                 .build();
         mCamera2CameraControl.startFocusAndMetering(action);
@@ -492,7 +492,7 @@
 
         SurfaceOrientedMeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(1.0f,
                 1.0f);
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0))
+        FocusMeteringAction action = new FocusMeteringAction.Builder(factory.createPoint(0, 0))
                 .build();
         mCamera2CameraControl.startFocusAndMetering(action);
         HandlerUtil.waitForLooperToIdle(mHandler);
@@ -538,7 +538,7 @@
 
         SurfaceOrientedMeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(1.0f,
                 1.0f);
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0))
+        FocusMeteringAction action = new FocusMeteringAction.Builder(factory.createPoint(0, 0))
                 .build();
         mCamera2CameraControl.startFocusAndMetering(action);
         HandlerUtil.waitForLooperToIdle(mHandler);
@@ -574,7 +574,7 @@
     public void cancelFocusAndMetering_AFNotInvolved_notCancelAF() throws InterruptedException {
         SurfaceOrientedMeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(1.0f,
                 1.0f);
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0),
+        FocusMeteringAction action = new FocusMeteringAction.Builder(factory.createPoint(0, 0),
                 FocusMeteringAction.FLAG_AE)
                 .build();
         mCamera2CameraControl.startFocusAndMetering(action);
@@ -592,7 +592,7 @@
     public void startFocus_afModeIsSetToAuto() throws InterruptedException {
         SurfaceOrientedMeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(1.0f,
                 1.0f);
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0))
+        FocusMeteringAction action = new FocusMeteringAction.Builder(factory.createPoint(0, 0))
                 .build();
         mCamera2CameraControl.startFocusAndMetering(action);
         HandlerUtil.waitForLooperToIdle(mHandler);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java
index c5467c9..01fe4c7 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java
@@ -35,9 +35,6 @@
 import androidx.camera.core.FocusMeteringResult;
 import androidx.camera.core.MeteringPoint;
 import androidx.camera.core.impl.CaptureConfig;
-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.concurrent.futures.CallbackToFutureAdapter;
 import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
 
@@ -154,20 +151,7 @@
 
         if (!mIsActive) {
             mExecutor.execute(() -> {
-                ListenableFuture<Void> future = cancelFocusAndMetering();
-                Futures.addCallback(future, new FutureCallback<Void>() {
-                    @Override
-                    public void onSuccess(@Nullable Void result) {
-                    }
-
-                    @Override
-                    public void onFailure(Throwable t) {
-                        if (!(t instanceof CameraControl.OperationCanceledException)) {
-                            // Throw the unexpected error.
-                            throw new RuntimeException(t);
-                        }
-                    }
-                }, CameraXExecutors.directExecutor());
+                cancelFocusAndMeteringWithoutAsyncResult();
             });
         }
     }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
index 0594c70..7c20bdc 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
@@ -461,8 +461,8 @@
     private static boolean isPossibleMod16FromAspectRatio(Size resolution, Rational aspectRatio) {
         int width = resolution.getWidth();
         int height = resolution.getHeight();
-        Rational invAspectRatio = new Rational(aspectRatio.getDenominator(),
-                aspectRatio.getNumerator());
+        Rational invAspectRatio = new Rational(/* numerator= */aspectRatio.getDenominator(),
+                /* denominator= */aspectRatio.getNumerator());
         if (width % 16 == 0 && height % 16 == 0) {
             return ratioIntersectsMod16Segment(Math.max(0, height - ALIGN16), width, aspectRatio)
                     || ratioIntersectsMod16Segment(Math.max(0, width - ALIGN16), height,
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/DisplayOrientedMeteringPointFactoryTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/DisplayOrientedMeteringPointFactoryTest.java
index e9d69d7..65967e2 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/DisplayOrientedMeteringPointFactoryTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/DisplayOrientedMeteringPointFactoryTest.java
@@ -24,7 +24,6 @@
 import android.os.Build;
 import android.view.Display;
 import android.view.Surface;
-import android.view.WindowManager;
 
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.CameraX;
@@ -63,7 +62,6 @@
     private static final float HEIGHT = 640;
     private static final String FRONT_CAMERA_ID = "1";
     private static final String BACK_CAMERA_ID = "0";
-    private Context mMockContext;
     private Display mMockDisplay;
     private static final CameraSelector FRONT_CAM =
             new CameraSelector.Builder().requireLensFacing(
@@ -104,10 +102,6 @@
 
         mMockDisplay = Mockito.mock(Display.class);
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
-        WindowManager mockWindowManager = Mockito.mock(WindowManager.class);
-        when(mockWindowManager.getDefaultDisplay()).thenReturn(mMockDisplay);
-        mMockContext = Mockito.mock(Context.class);
-        when(mMockContext.getSystemService(Context.WINDOW_SERVICE)).thenReturn(mockWindowManager);
     }
 
     @After
@@ -118,7 +112,7 @@
     @Test
     public void defaultAreaSize() {
         DisplayOrientedMeteringPointFactory factory = new DisplayOrientedMeteringPointFactory(
-                mMockContext, BACK_CAM, WIDTH, HEIGHT);
+                mMockDisplay, BACK_CAM, WIDTH, HEIGHT);
 
         MeteringPoint point = factory.createPoint(0, 0);
         assertThat(point.getSize()).isEqualTo(MeteringPointFactory.getDefaultPointSize());
@@ -128,7 +122,7 @@
     @Test
     public void createPointWithValidAreaSize() {
         DisplayOrientedMeteringPointFactory factory = new DisplayOrientedMeteringPointFactory(
-                mMockContext, BACK_CAM, WIDTH, HEIGHT);
+                mMockDisplay, BACK_CAM, WIDTH, HEIGHT);
 
         final float areaSize = 0.2f;
         MeteringPoint point = factory.createPoint(0, 0, areaSize);
@@ -141,7 +135,7 @@
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
 
         DisplayOrientedMeteringPointFactory factory = new DisplayOrientedMeteringPointFactory(
-                mMockContext, BACK_CAM, WIDTH, HEIGHT);
+                mMockDisplay, BACK_CAM, WIDTH, HEIGHT);
 
         MeteringPoint meteringPoint = factory.createPoint(0f, 0f);
         assertThat(meteringPoint.getX()).isEqualTo(0f);
@@ -167,7 +161,7 @@
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
 
         DisplayOrientedMeteringPointFactory factory = new DisplayOrientedMeteringPointFactory(
-                mMockContext, FRONT_CAM, WIDTH, HEIGHT);
+                mMockDisplay, FRONT_CAM, WIDTH, HEIGHT);
 
         MeteringPoint meteringPoint = factory.createPoint(0f, 0f);
         assertThat(meteringPoint.getX()).isEqualTo(1f);
@@ -193,7 +187,7 @@
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_90);
 
         DisplayOrientedMeteringPointFactory factory = new DisplayOrientedMeteringPointFactory(
-                mMockContext, BACK_CAM, WIDTH, HEIGHT);
+                mMockDisplay, BACK_CAM, WIDTH, HEIGHT);
 
         MeteringPoint meteringPoint = factory.createPoint(0f, 0f);
         assertThat(meteringPoint.getX()).isEqualTo(0f);
@@ -219,7 +213,7 @@
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_90);
 
         DisplayOrientedMeteringPointFactory factory = new DisplayOrientedMeteringPointFactory(
-                mMockContext, FRONT_CAM, WIDTH, HEIGHT);
+                mMockDisplay, FRONT_CAM, WIDTH, HEIGHT);
 
         MeteringPoint meteringPoint = factory.createPoint(0f, 0f);
         assertThat(meteringPoint.getX()).isEqualTo(1f);
@@ -245,7 +239,7 @@
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_180);
 
         DisplayOrientedMeteringPointFactory factory = new DisplayOrientedMeteringPointFactory(
-                mMockContext, BACK_CAM, WIDTH, HEIGHT);
+                mMockDisplay, BACK_CAM, WIDTH, HEIGHT);
 
         MeteringPoint meteringPoint = factory.createPoint(0f, 0f);
         assertThat(meteringPoint.getX()).isEqualTo(1f);
@@ -271,7 +265,7 @@
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_180);
 
         DisplayOrientedMeteringPointFactory factory = new DisplayOrientedMeteringPointFactory(
-                mMockContext, FRONT_CAM, WIDTH, HEIGHT);
+                mMockDisplay, FRONT_CAM, WIDTH, HEIGHT);
 
         MeteringPoint meteringPoint = factory.createPoint(0f, 0f);
         assertThat(meteringPoint.getX()).isEqualTo(0f);
@@ -297,7 +291,7 @@
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_270);
 
         DisplayOrientedMeteringPointFactory factory = new DisplayOrientedMeteringPointFactory(
-                mMockContext, BACK_CAM, WIDTH, HEIGHT);
+                mMockDisplay, BACK_CAM, WIDTH, HEIGHT);
 
         MeteringPoint meteringPoint = factory.createPoint(0f, 0f);
         assertThat(meteringPoint.getX()).isEqualTo(1f);
@@ -323,7 +317,7 @@
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_270);
 
         DisplayOrientedMeteringPointFactory factory = new DisplayOrientedMeteringPointFactory(
-                mMockContext, FRONT_CAM, WIDTH, HEIGHT);
+                mMockDisplay, FRONT_CAM, WIDTH, HEIGHT);
 
         MeteringPoint meteringPoint = factory.createPoint(0f, 0f);
         assertThat(meteringPoint.getX()).isEqualTo(0f);
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/FocusMeteringControlTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/FocusMeteringControlTest.java
index c96cfba..aacdb60 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/FocusMeteringControlTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/FocusMeteringControlTest.java
@@ -301,7 +301,7 @@
     @Test
     public void startFocusAndMetering_defaultPoint_3ARectssAreCorrect() {
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1).build(),
+                new FocusMeteringAction.Builder(mPoint1).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
 
         MeteringRectangle[] afRects = getAfRects(mFocusMeteringControl);
@@ -320,7 +320,7 @@
     public void startFocusAndMetering_multiplePoint_3ARectsAreCorrect() {
         // Max AF count = 3, Max AE count = 3, Max AWB count = 1
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1)
+                new FocusMeteringAction.Builder(mPoint1)
                         .addPoint(mPoint2)
                         .addPoint(mPoint3)
                         .build(), PREVIEW_ASPECT_RATIO_4_X_3);
@@ -347,7 +347,7 @@
     public void startFocusAndMetering_multiplePointVariousModes() {
         // Max AF count = 3, Max AE count = 3, Max AWB count = 1
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1, FLAG_AWB)
+                new FocusMeteringAction.Builder(mPoint1, FLAG_AWB)
                         .addPoint(mPoint2, FLAG_AF | FLAG_AE)
                         .addPoint(mPoint3, FLAG_AF | FLAG_AE | FLAG_AWB)
                         .build(), PREVIEW_ASPECT_RATIO_4_X_3);
@@ -372,7 +372,7 @@
     public void startFocusAndMetering_multiplePointVariousModes2() {
         // Max AF count = 3, Max AE count = 3, Max AWB count = 1
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1, FLAG_AF)
+                new FocusMeteringAction.Builder(mPoint1, FLAG_AF)
                         .addPoint(mPoint2, FLAG_AWB)
                         .addPoint(mPoint3, FLAG_AE)
                         .build(), PREVIEW_ASPECT_RATIO_4_X_3);
@@ -401,7 +401,7 @@
 
         MeteringPoint centorPt = mPointFactory.createPoint(0.5f, 0.5f);
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(centorPt).build(),
+                new FocusMeteringAction.Builder(centorPt).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
         MeteringRectangle[] afRects = getAfRects(mFocusMeteringControl);
 
@@ -421,7 +421,7 @@
         // use 16:9 preview aspect ratio
         Rational previewAspectRatio = new Rational(16, 9);
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1).build(),
+                new FocusMeteringAction.Builder(mPoint1).build(),
                 previewAspectRatio);
         MeteringRectangle[] afRects = getAfRects(mFocusMeteringControl);
 
@@ -437,7 +437,7 @@
         mFocusMeteringControl = initFocusMeteringControl(CAMERA1_ID);
 
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1).build(),
+                new FocusMeteringAction.Builder(mPoint1).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
         MeteringRectangle[] afRects = getAfRects(mFocusMeteringControl);
 
@@ -458,7 +458,7 @@
                 new SurfaceOrientedMeteringPointFactory(1.0f, 1.0f, imageAnalysis);
 
         MeteringPoint point = factory.createPoint(0, 0);
-        mFocusMeteringControl.startFocusAndMetering(FocusMeteringAction.Builder.from(point).build(),
+        mFocusMeteringControl.startFocusAndMetering(new FocusMeteringAction.Builder(point).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
         MeteringRectangle[] afRects = getAfRects(mFocusMeteringControl);
 
@@ -473,7 +473,7 @@
         MeteringPoint point2 = mPointFactory.createPoint(0.5f, 0.5f, 0.5f);
         MeteringPoint point3 = mPointFactory.createPoint(0.5f, 0.5f, 0.1f);
 
-        mFocusMeteringControl.startFocusAndMetering(FocusMeteringAction.Builder.from(point1)
+        mFocusMeteringControl.startFocusAndMetering(new FocusMeteringAction.Builder(point1)
                 .addPoint(point2)
                 .addPoint(point3).build(), PREVIEW_ASPECT_RATIO_4_X_3);
         MeteringRectangle[] afRects = getAfRects(mFocusMeteringControl);
@@ -497,7 +497,7 @@
 
     @Test
     public void withAFPoints_AFIsTriggered() {
-        mFocusMeteringControl.startFocusAndMetering(FocusMeteringAction.Builder.from(mPoint1,
+        mFocusMeteringControl.startFocusAndMetering(new FocusMeteringAction.Builder(mPoint1,
                 FLAG_AF | FLAG_AE | FLAG_AWB).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
 
@@ -505,19 +505,19 @@
         Mockito.reset(mFocusMeteringControl);
 
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1, FLAG_AF).build(),
+                new FocusMeteringAction.Builder(mPoint1, FLAG_AF).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
         verify(mFocusMeteringControl).triggerAf();
         Mockito.reset(mFocusMeteringControl);
 
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1, FLAG_AF | FLAG_AE).build(),
+                new FocusMeteringAction.Builder(mPoint1, FLAG_AF | FLAG_AE).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
         verify(mFocusMeteringControl).triggerAf();
         Mockito.reset(mFocusMeteringControl);
 
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1, FLAG_AF | FLAG_AWB).build(),
+                new FocusMeteringAction.Builder(mPoint1, FLAG_AF | FLAG_AWB).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
         verify(mFocusMeteringControl).triggerAf();
         Mockito.reset(mFocusMeteringControl);
@@ -526,25 +526,25 @@
     @Test
     public void withoutAFPoints_AFIsNotTriggered() {
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1, FLAG_AE).build(),
+                new FocusMeteringAction.Builder(mPoint1, FLAG_AE).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
         verify(mFocusMeteringControl, never()).triggerAf();
         Mockito.reset(mFocusMeteringControl);
 
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1, FLAG_AWB).build(),
+                new FocusMeteringAction.Builder(mPoint1, FLAG_AWB).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
         verify(mFocusMeteringControl, never()).triggerAf();
         Mockito.reset(mFocusMeteringControl);
 
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1, FLAG_AE).build(),
+                new FocusMeteringAction.Builder(mPoint1, FLAG_AE).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
         verify(mFocusMeteringControl, never()).triggerAf();
         Mockito.reset(mFocusMeteringControl);
 
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1,
+                new FocusMeteringAction.Builder(mPoint1,
                         FLAG_AE | FLAG_AWB).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
         verify(mFocusMeteringControl, never()).triggerAf();
@@ -554,7 +554,7 @@
     @Test
     public void updateSessionConfigIsCalled() {
         mFocusMeteringControl.startFocusAndMetering(
-                FocusMeteringAction.Builder.from(mPoint1).build(),
+                new FocusMeteringAction.Builder(mPoint1).build(),
                 PREVIEW_ASPECT_RATIO_4_X_3);
 
         verify(mCamera2CameraControl, times(1)).updateSessionConfig();
@@ -564,7 +564,7 @@
     public void autoCancelDuration_cancelIsCalled() throws InterruptedException {
         mFocusMeteringControl = spy(mFocusMeteringControl);
         final long autocancelDuration = 500;
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .setAutoCancelDuration(autocancelDuration, TimeUnit.MILLISECONDS)
                 .build();
 
@@ -581,7 +581,7 @@
         mFocusMeteringControl = spy(mFocusMeteringControl);
         final long autocancelDuration = 500;
 
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .setAutoCancelDuration(autocancelDuration, TimeUnit.MILLISECONDS)
                 .disableAutoCancel()
                 .build();
@@ -687,7 +687,7 @@
     @Test
     public void startFocusMeteringAEAWB_regionsUpdated_completesWithFocusFalse()
             throws ExecutionException, InterruptedException, TimeoutException {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FLAG_AE | FLAG_AWB)
                 .build();
         ListenableFuture<FocusMeteringResult> future =
@@ -706,7 +706,7 @@
     @Test
     public void startFocusMeteringAE_regionsUpdated_completesWithFocusFalse()
             throws ExecutionException, InterruptedException, TimeoutException {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FLAG_AE).build();
 
         ListenableFuture<FocusMeteringResult> future2 =
@@ -723,7 +723,7 @@
     @Test
     public void startFocusMeteringAWB_regionsUpdated_completesWithFocusFalse()
             throws ExecutionException, InterruptedException, TimeoutException {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FLAG_AWB)
                 .build();
 
@@ -741,7 +741,7 @@
     @Test
     public void startFocusMetering_AFLockedWith3ARegionsUpdated_completesWithfocusTrue()
             throws ExecutionException, InterruptedException, TimeoutException {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1).build();
 
         ListenableFuture<FocusMeteringResult> future =
                 mFocusMeteringControl.startFocusAndMetering(action,
@@ -764,7 +764,7 @@
     @Test
     public void startFocusMetering_AFLockedThen3ARegions_completesWithFocusTrue()
             throws ExecutionException, InterruptedException, TimeoutException {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1).build();
 
         ListenableFuture<FocusMeteringResult> future =
                 mFocusMeteringControl.startFocusAndMetering(action,
@@ -789,7 +789,7 @@
     @Test
     public void startFocusMetering_NotAFLocked_completesWithFocusFalse()
             throws ExecutionException, InterruptedException, TimeoutException {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1).build();
 
         ListenableFuture<FocusMeteringResult> future =
                 mFocusMeteringControl.startFocusAndMetering(action, PREVIEW_ASPECT_RATIO_4_X_3);
@@ -812,7 +812,7 @@
     public void startFocusMeteringAFOnly_AfRegionUpdated_completesWithFocusTrue()
             throws ExecutionException, InterruptedException, TimeoutException {
         FocusMeteringAction action =
-                FocusMeteringAction.Builder.from(mPoint1, FLAG_AF).build();
+                new FocusMeteringAction.Builder(mPoint1, FLAG_AF).build();
 
         ListenableFuture<FocusMeteringResult> future =
                 mFocusMeteringControl.startFocusAndMetering(action, PREVIEW_ASPECT_RATIO_4_X_3);
@@ -834,7 +834,7 @@
             throws ExecutionException, InterruptedException, TimeoutException {
         // Use camera which does not support AF_AUTO
         FocusMeteringControl focusMeteringControl = initFocusMeteringControl(CAMERA2_ID);
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .build();
 
         ListenableFuture<FocusMeteringResult> result = focusMeteringControl.startFocusAndMetering(
@@ -852,7 +852,7 @@
 
     @Test
     public void startFocusMetering_cancelBeforeCompleted_failWithOperationCancelledOperation() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1).build();
 
         ListenableFuture<FocusMeteringResult> future = mFocusMeteringControl.startFocusAndMetering(
                 action, PREVIEW_ASPECT_RATIO_4_X_3);
@@ -869,7 +869,7 @@
     @Test
     public void startThenCancelThenStart_previous2FuturesFailsWithOperationCancelled()
             throws ExecutionException, InterruptedException, TimeoutException {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .build();
 
         ListenableFuture<FocusMeteringResult> result1 =
@@ -900,7 +900,7 @@
     @Test
     public void startMultipleActions_cancelNonLatest()
             throws ExecutionException, InterruptedException, TimeoutException {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .build();
 
         ListenableFuture<FocusMeteringResult> result1 =
@@ -931,7 +931,7 @@
     @Test
     public void startFocusMetering_focusedThenCancel_futureStillCompletes()
             throws ExecutionException, InterruptedException, TimeoutException {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .build();
 
         ListenableFuture<FocusMeteringResult> result =
@@ -955,7 +955,7 @@
 
     @Test
     public void cancelFocusAndMetering_regionIsReset() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FLAG_AF | FLAG_AE | FLAG_AWB)
                 .addPoint(mPoint2, FLAG_AF | FLAG_AE | FLAG_AWB)
                 .build();
@@ -982,7 +982,7 @@
 
     @Test
     public void cancelFocusAndMetering_updateSessionIsCalled() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FLAG_AF | FLAG_AE | FLAG_AWB)
                 .addPoint(mPoint2, FLAG_AF | FLAG_AE | FLAG_AWB)
                 .build();
@@ -998,7 +998,7 @@
     @Test
     public void cancelFocusAndMetering_triggerCancelAfProperly() {
         // If AF is enabled, cancel operation needs to call cancelAfAeTriggerInternal(true, false)
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FLAG_AF | FLAG_AE | FLAG_AWB)
                 .build();
 
@@ -1007,19 +1007,19 @@
         mFocusMeteringControl.cancelFocusAndMetering();
         verify(mFocusMeteringControl, times(1)).cancelAfAeTrigger(true, false);
 
-        action = FocusMeteringAction.Builder.from(mPoint1, FLAG_AF | FLAG_AE).build();
+        action = new FocusMeteringAction.Builder(mPoint1, FLAG_AF | FLAG_AE).build();
         mFocusMeteringControl.startFocusAndMetering(action, PREVIEW_ASPECT_RATIO_4_X_3);
         Mockito.reset(mFocusMeteringControl);
         mFocusMeteringControl.cancelFocusAndMetering();
         verify(mFocusMeteringControl, times(1)).cancelAfAeTrigger(true, false);
 
-        action = FocusMeteringAction.Builder.from(mPoint1, FLAG_AF | FLAG_AWB).build();
+        action = new FocusMeteringAction.Builder(mPoint1, FLAG_AF | FLAG_AWB).build();
         mFocusMeteringControl.startFocusAndMetering(action, PREVIEW_ASPECT_RATIO_4_X_3);
         Mockito.reset(mFocusMeteringControl);
         mFocusMeteringControl.cancelFocusAndMetering();
         verify(mFocusMeteringControl, times(1)).cancelAfAeTrigger(true, false);
 
-        action = FocusMeteringAction.Builder.from(mPoint1, FLAG_AF)
+        action = new FocusMeteringAction.Builder(mPoint1, FLAG_AF)
                 .build();
         mFocusMeteringControl.startFocusAndMetering(action, PREVIEW_ASPECT_RATIO_4_X_3);
         Mockito.reset(mFocusMeteringControl);
@@ -1029,20 +1029,20 @@
 
     @Test
     public void cancelFocusAndMetering_AFNotInvolved_cancelAfNotTriggered() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1, FLAG_AE).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1, FLAG_AE).build();
 
         mFocusMeteringControl.startFocusAndMetering(action, PREVIEW_ASPECT_RATIO_4_X_3);
         Mockito.reset(mFocusMeteringControl);
         mFocusMeteringControl.cancelFocusAndMetering();
         verify(mFocusMeteringControl, never()).cancelAfAeTrigger(true, false);
 
-        action = FocusMeteringAction.Builder.from(mPoint1, FLAG_AWB).build();
+        action = new FocusMeteringAction.Builder(mPoint1, FLAG_AWB).build();
         mFocusMeteringControl.startFocusAndMetering(action, PREVIEW_ASPECT_RATIO_4_X_3);
         Mockito.reset(mFocusMeteringControl);
         mFocusMeteringControl.cancelFocusAndMetering();
         verify(mFocusMeteringControl, never()).cancelAfAeTrigger(true, false);
 
-        action = FocusMeteringAction.Builder.from(mPoint1, FLAG_AE | FLAG_AWB).build();
+        action = new FocusMeteringAction.Builder(mPoint1, FLAG_AE | FLAG_AWB).build();
         mFocusMeteringControl.startFocusAndMetering(action, PREVIEW_ASPECT_RATIO_4_X_3);
         Mockito.reset(mFocusMeteringControl);
         mFocusMeteringControl.cancelFocusAndMetering();
@@ -1051,7 +1051,7 @@
 
     @Test
     public void cancelFocusMetering_actionIsCancelledAndfutureCompletes() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1).build();
         ListenableFuture<FocusMeteringResult> actionResult =
                 mFocusMeteringControl.startFocusAndMetering(action,
                         PREVIEW_ASPECT_RATIO_4_X_3);
@@ -1077,7 +1077,7 @@
         mFocusMeteringControl = spy(mFocusMeteringControl);
         final long autocancelDuration = 500;
 
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .setAutoCancelDuration(autocancelDuration, TimeUnit.MILLISECONDS)
                 .build();
 
@@ -1094,7 +1094,7 @@
 
     @Test
     public void startFocusMetering_isAfAutoModeIsTrue() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1).build();
 
         verifyAfMode(CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
 
@@ -1113,7 +1113,7 @@
 
     @Test
     public void startFocusMetering_AfNotInvolved_isAfAutoModeIsSet() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FLAG_AE | FLAG_AWB).build();
 
         verifyAfMode(CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
@@ -1123,7 +1123,7 @@
 
     @Test
     public void startAndThenCancel_isAfAutoModeIsFalse() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1).build();
         mFocusMeteringControl.startFocusAndMetering(action, PREVIEW_ASPECT_RATIO_4_X_3);
         mFocusMeteringControl.cancelFocusAndMetering();
         verifyAfMode(CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
@@ -1132,7 +1132,7 @@
     @Test
     public void startFocusMeteringAFAEAWB_noPointsAreSupported_failFuture() {
         FocusMeteringControl focusMeteringControl = initFocusMeteringControl(CAMERA3_ID);
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FLAG_AF | FLAG_AE | FLAG_AWB).build();
 
         ListenableFuture<FocusMeteringResult> future =
@@ -1153,7 +1153,7 @@
     @Test
     public void startFocusMeteringAEAWB_noPointsAreSupported_failFuture() {
         FocusMeteringControl focusMeteringControl = initFocusMeteringControl(CAMERA3_ID);
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FLAG_AE | FLAG_AWB).build();
 
         ListenableFuture<FocusMeteringResult> future =
@@ -1174,7 +1174,7 @@
     @Test
     public void startFocusMeteringAFAWB_noPointsAreSupported_failFuture() {
         FocusMeteringControl focusMeteringControl = initFocusMeteringControl(CAMERA3_ID);
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FLAG_AF | FLAG_AWB).build();
 
         ListenableFuture<FocusMeteringResult> future =
@@ -1196,8 +1196,8 @@
             throws ExecutionException, InterruptedException, TimeoutException {
         // Camera0 only support 3 AF, 3 AE, 1 AWB regions, here we try to have 1 AE region, 2 AWB
         // regions.  it should still complete the future.
-        FocusMeteringAction action = FocusMeteringAction.Builder
-                .from(mPoint1, FLAG_AE | FLAG_AWB)
+        FocusMeteringAction action =
+                new FocusMeteringAction.Builder(mPoint1, FLAG_AE | FLAG_AWB)
                 .addPoint(mPoint2, FLAG_AWB)
                 .build();
 
diff --git a/camera/camera-core/api/1.0.0-alpha08.txt b/camera/camera-core/api/1.0.0-alpha08.txt
new file mode 100644
index 0000000..8a23bc6
--- /dev/null
+++ b/camera/camera-core/api/1.0.0-alpha08.txt
@@ -0,0 +1,244 @@
+// Signature format: 3.0
+package androidx.camera.core {
+
+  public class AspectRatio {
+    field public static final int RATIO_16_9 = 1; // 0x1
+    field public static final int RATIO_4_3 = 0; // 0x0
+  }
+
+  public interface Camera {
+    method public androidx.camera.core.CameraControl getCameraControl();
+    method public androidx.camera.core.CameraInfo getCameraInfo();
+  }
+
+  public interface CameraControl {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
+  }
+
+  public static final class CameraControl.OperationCanceledException extends java.lang.Exception {
+  }
+
+  public interface CameraInfo {
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getLinearZoom();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getMaxZoomRatio();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getMinZoomRatio();
+    method public int getSensorRotationDegrees();
+    method public int getSensorRotationDegrees(int);
+    method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getZoomRatio();
+    method public boolean hasFlashUnit();
+  }
+
+  public final class CameraInfoUnavailableException extends java.lang.Exception {
+  }
+
+  public final class CameraSelector {
+    field public static final androidx.camera.core.CameraSelector DEFAULT_BACK_CAMERA;
+    field public static final androidx.camera.core.CameraSelector DEFAULT_FRONT_CAMERA;
+    field public static final int LENS_FACING_BACK = 1; // 0x1
+    field public static final int LENS_FACING_FRONT = 0; // 0x0
+  }
+
+  public static final class CameraSelector.Builder {
+    ctor public CameraSelector.Builder();
+    method public androidx.camera.core.CameraSelector build();
+    method public androidx.camera.core.CameraSelector.Builder requireLensFacing(int);
+  }
+
+  public final class CameraXConfig {
+    field public static final androidx.camera.core.impl.Config.Option<java.lang.Class<?>!> OPTION_TARGET_CLASS;
+    field public static final androidx.camera.core.impl.Config.Option<java.lang.String!> OPTION_TARGET_NAME;
+  }
+
+  public static final class CameraXConfig.Builder {
+    ctor public CameraXConfig.Builder();
+    method public androidx.camera.core.CameraXConfig build();
+    method public static androidx.camera.core.CameraXConfig.Builder fromConfig(androidx.camera.core.CameraXConfig);
+    method public androidx.camera.core.CameraXConfig.Builder setCameraExecutor(java.util.concurrent.Executor);
+  }
+
+  public static interface CameraXConfig.Provider {
+    method public androidx.camera.core.CameraXConfig getCameraXConfig();
+  }
+
+  public final class DisplayOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public DisplayOrientedMeteringPointFactory(android.content.Context, androidx.camera.core.CameraSelector, float, float);
+    ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraSelector, float, float);
+  }
+
+  public interface ExtendableBuilder<T> {
+    method public T build();
+  }
+
+  public final class FocusMeteringAction {
+    method public long getAutoCancelDurationInMillis();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAe();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAf();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAwb();
+    method public boolean isAutoCancelEnabled();
+    field public static final int FLAG_AE = 2; // 0x2
+    field public static final int FLAG_AF = 1; // 0x1
+    field public static final int FLAG_AWB = 4; // 0x4
+  }
+
+  public static class FocusMeteringAction.Builder {
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint);
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint, int);
+    method public androidx.camera.core.FocusMeteringAction build();
+    method public androidx.camera.core.FocusMeteringAction.Builder disableAutoCancel();
+    method public static androidx.camera.core.FocusMeteringAction.Builder from(androidx.camera.core.MeteringPoint);
+    method public static androidx.camera.core.FocusMeteringAction.Builder from(androidx.camera.core.MeteringPoint, int);
+    method public androidx.camera.core.FocusMeteringAction.Builder setAutoCancelDuration(@IntRange(from=1) long, java.util.concurrent.TimeUnit);
+  }
+
+  public final class FocusMeteringResult {
+    method public boolean isFocusSuccessful();
+  }
+
+  public final class ImageAnalysis extends androidx.camera.core.UseCase {
+    method public void clearAnalyzer();
+    method public void setAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+    method public void setTargetRotation(int);
+    field public static final int STRATEGY_BLOCK_PRODUCER = 1; // 0x1
+    field public static final int STRATEGY_KEEP_ONLY_LATEST = 0; // 0x0
+  }
+
+  public static interface ImageAnalysis.Analyzer {
+    method public void analyze(androidx.camera.core.ImageProxy);
+  }
+
+  public static final class ImageAnalysis.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.ImageAnalysis> {
+    ctor public ImageAnalysis.Builder();
+    method public androidx.camera.core.ImageAnalysis build();
+    method public androidx.camera.core.ImageAnalysis.Builder setBackgroundExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageAnalysis.Builder setBackpressureStrategy(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setImageQueueDepth(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetName(String);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetRotation(int);
+  }
+
+  public class ImageCapture extends androidx.camera.core.UseCase {
+    method public int getFlashMode();
+    method public void setFlashMode(int);
+    method public void setTargetAspectRatioCustom(android.util.Rational);
+    method public void setTargetRotation(int);
+    method public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
+    method public void takePicture(java.io.File, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    method public void takePicture(java.io.File, androidx.camera.core.ImageCapture.Metadata, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    field public static final int CAPTURE_MODE_MAXIMIZE_QUALITY = 0; // 0x0
+    field public static final int CAPTURE_MODE_MINIMIZE_LATENCY = 1; // 0x1
+    field public static final int ERROR_CAMERA_CLOSED = 3; // 0x3
+    field public static final int ERROR_CAPTURE_FAILED = 2; // 0x2
+    field public static final int ERROR_FILE_IO = 1; // 0x1
+    field public static final int ERROR_INVALID_CAMERA = 4; // 0x4
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int FLASH_MODE_AUTO = 0; // 0x0
+    field public static final int FLASH_MODE_OFF = 2; // 0x2
+    field public static final int FLASH_MODE_ON = 1; // 0x1
+  }
+
+  public static final class ImageCapture.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.ImageCapture> {
+    ctor public ImageCapture.Builder();
+    method public androidx.camera.core.ImageCapture build();
+    method public androidx.camera.core.ImageCapture.Builder setCaptureMode(int);
+    method public androidx.camera.core.ImageCapture.Builder setFlashMode(int);
+    method public androidx.camera.core.ImageCapture.Builder setIoExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageCapture.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.ImageCapture.Builder setTargetName(String);
+    method public androidx.camera.core.ImageCapture.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageCapture.Builder setTargetRotation(int);
+  }
+
+  public static final class ImageCapture.Metadata {
+    ctor public ImageCapture.Metadata();
+    method public android.location.Location? getLocation();
+    method public boolean isReversedHorizontal();
+    method public boolean isReversedVertical();
+    method public void setLocation(android.location.Location?);
+    method public void setReversedHorizontal(boolean);
+    method public void setReversedVertical(boolean);
+  }
+
+  public abstract static class ImageCapture.OnImageCapturedCallback {
+    ctor public ImageCapture.OnImageCapturedCallback();
+    method public void onCaptureSuccess(androidx.camera.core.ImageProxy);
+    method public void onError(int, String, Throwable?);
+  }
+
+  public static interface ImageCapture.OnImageSavedCallback {
+    method public void onError(int, String, Throwable?);
+    method public void onImageSaved(java.io.File);
+  }
+
+  public interface ImageInfo {
+    method public int getRotationDegrees();
+    method public long getTimestamp();
+  }
+
+  public interface ImageProxy extends java.lang.AutoCloseable {
+    method public void close();
+    method public android.graphics.Rect getCropRect();
+    method public int getFormat();
+    method public int getHeight();
+    method public androidx.camera.core.ImageInfo getImageInfo();
+    method public androidx.camera.core.ImageProxy.PlaneProxy![] getPlanes();
+    method public int getWidth();
+    method public void setCropRect(android.graphics.Rect?);
+  }
+
+  public static interface ImageProxy.PlaneProxy {
+    method public java.nio.ByteBuffer getBuffer();
+    method public int getPixelStride();
+    method public int getRowStride();
+  }
+
+  public class MeteringPoint {
+    method public float getSize();
+  }
+
+  public abstract class MeteringPointFactory {
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float);
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float, float);
+    method public static float getDefaultPointSize();
+  }
+
+  public class Preview extends androidx.camera.core.UseCase {
+    method @UiThread public androidx.camera.core.Preview.PreviewSurfaceProvider? getPreviewSurfaceProvider();
+    method @UiThread public void setPreviewSurfaceProvider(java.util.concurrent.Executor, androidx.camera.core.Preview.PreviewSurfaceProvider?);
+    method @UiThread public void setPreviewSurfaceProvider(androidx.camera.core.Preview.PreviewSurfaceProvider?);
+  }
+
+  public static final class Preview.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.Preview> {
+    ctor public Preview.Builder();
+    method public androidx.camera.core.Preview build();
+    method public androidx.camera.core.Preview.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.Preview.Builder setTargetName(String);
+    method public androidx.camera.core.Preview.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.Preview.Builder setTargetRotation(int);
+  }
+
+  public static interface Preview.PreviewSurfaceProvider {
+    method public com.google.common.util.concurrent.ListenableFuture<android.view.Surface!> provideSurface(android.util.Size, com.google.common.util.concurrent.ListenableFuture<java.lang.Void!>);
+  }
+
+  public class SurfaceOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public SurfaceOrientedMeteringPointFactory(float, float);
+    ctor public SurfaceOrientedMeteringPointFactory(float, float, androidx.camera.core.UseCase);
+  }
+
+  public class TorchState {
+    field public static final int OFF = 0; // 0x0
+    field public static final int ON = 1; // 0x1
+  }
+
+  public abstract class UseCase {
+  }
+
+}
+
diff --git a/camera/camera-core/api/api_lint.ignore b/camera/camera-core/api/api_lint.ignore
new file mode 100644
index 0000000..1a0493c
--- /dev/null
+++ b/camera/camera-core/api/api_lint.ignore
@@ -0,0 +1,9 @@
+// Baseline format: 1.0
+HiddenTypeParameter: androidx.camera.core.CameraXConfig#OPTION_TARGET_CLASS:
+    Field CameraXConfig.OPTION_TARGET_CLASS references hidden type androidx.camera.core.impl.Config.Option<java.lang.Class<?>>.
+HiddenTypeParameter: androidx.camera.core.CameraXConfig#OPTION_TARGET_NAME:
+    Field CameraXConfig.OPTION_TARGET_NAME references hidden type androidx.camera.core.impl.Config.Option<java.lang.String>.
+
+
+TopLevelBuilder: androidx.camera.core.ExtendableBuilder:
+    Builder should be defined as inner class: androidx.camera.core.ExtendableBuilder
diff --git a/camera/camera-core/api/current.txt b/camera/camera-core/api/current.txt
new file mode 100644
index 0000000..8a23bc6
--- /dev/null
+++ b/camera/camera-core/api/current.txt
@@ -0,0 +1,244 @@
+// Signature format: 3.0
+package androidx.camera.core {
+
+  public class AspectRatio {
+    field public static final int RATIO_16_9 = 1; // 0x1
+    field public static final int RATIO_4_3 = 0; // 0x0
+  }
+
+  public interface Camera {
+    method public androidx.camera.core.CameraControl getCameraControl();
+    method public androidx.camera.core.CameraInfo getCameraInfo();
+  }
+
+  public interface CameraControl {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
+  }
+
+  public static final class CameraControl.OperationCanceledException extends java.lang.Exception {
+  }
+
+  public interface CameraInfo {
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getLinearZoom();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getMaxZoomRatio();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getMinZoomRatio();
+    method public int getSensorRotationDegrees();
+    method public int getSensorRotationDegrees(int);
+    method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getZoomRatio();
+    method public boolean hasFlashUnit();
+  }
+
+  public final class CameraInfoUnavailableException extends java.lang.Exception {
+  }
+
+  public final class CameraSelector {
+    field public static final androidx.camera.core.CameraSelector DEFAULT_BACK_CAMERA;
+    field public static final androidx.camera.core.CameraSelector DEFAULT_FRONT_CAMERA;
+    field public static final int LENS_FACING_BACK = 1; // 0x1
+    field public static final int LENS_FACING_FRONT = 0; // 0x0
+  }
+
+  public static final class CameraSelector.Builder {
+    ctor public CameraSelector.Builder();
+    method public androidx.camera.core.CameraSelector build();
+    method public androidx.camera.core.CameraSelector.Builder requireLensFacing(int);
+  }
+
+  public final class CameraXConfig {
+    field public static final androidx.camera.core.impl.Config.Option<java.lang.Class<?>!> OPTION_TARGET_CLASS;
+    field public static final androidx.camera.core.impl.Config.Option<java.lang.String!> OPTION_TARGET_NAME;
+  }
+
+  public static final class CameraXConfig.Builder {
+    ctor public CameraXConfig.Builder();
+    method public androidx.camera.core.CameraXConfig build();
+    method public static androidx.camera.core.CameraXConfig.Builder fromConfig(androidx.camera.core.CameraXConfig);
+    method public androidx.camera.core.CameraXConfig.Builder setCameraExecutor(java.util.concurrent.Executor);
+  }
+
+  public static interface CameraXConfig.Provider {
+    method public androidx.camera.core.CameraXConfig getCameraXConfig();
+  }
+
+  public final class DisplayOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public DisplayOrientedMeteringPointFactory(android.content.Context, androidx.camera.core.CameraSelector, float, float);
+    ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraSelector, float, float);
+  }
+
+  public interface ExtendableBuilder<T> {
+    method public T build();
+  }
+
+  public final class FocusMeteringAction {
+    method public long getAutoCancelDurationInMillis();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAe();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAf();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAwb();
+    method public boolean isAutoCancelEnabled();
+    field public static final int FLAG_AE = 2; // 0x2
+    field public static final int FLAG_AF = 1; // 0x1
+    field public static final int FLAG_AWB = 4; // 0x4
+  }
+
+  public static class FocusMeteringAction.Builder {
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint);
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint, int);
+    method public androidx.camera.core.FocusMeteringAction build();
+    method public androidx.camera.core.FocusMeteringAction.Builder disableAutoCancel();
+    method public static androidx.camera.core.FocusMeteringAction.Builder from(androidx.camera.core.MeteringPoint);
+    method public static androidx.camera.core.FocusMeteringAction.Builder from(androidx.camera.core.MeteringPoint, int);
+    method public androidx.camera.core.FocusMeteringAction.Builder setAutoCancelDuration(@IntRange(from=1) long, java.util.concurrent.TimeUnit);
+  }
+
+  public final class FocusMeteringResult {
+    method public boolean isFocusSuccessful();
+  }
+
+  public final class ImageAnalysis extends androidx.camera.core.UseCase {
+    method public void clearAnalyzer();
+    method public void setAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+    method public void setTargetRotation(int);
+    field public static final int STRATEGY_BLOCK_PRODUCER = 1; // 0x1
+    field public static final int STRATEGY_KEEP_ONLY_LATEST = 0; // 0x0
+  }
+
+  public static interface ImageAnalysis.Analyzer {
+    method public void analyze(androidx.camera.core.ImageProxy);
+  }
+
+  public static final class ImageAnalysis.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.ImageAnalysis> {
+    ctor public ImageAnalysis.Builder();
+    method public androidx.camera.core.ImageAnalysis build();
+    method public androidx.camera.core.ImageAnalysis.Builder setBackgroundExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageAnalysis.Builder setBackpressureStrategy(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setImageQueueDepth(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetName(String);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetRotation(int);
+  }
+
+  public class ImageCapture extends androidx.camera.core.UseCase {
+    method public int getFlashMode();
+    method public void setFlashMode(int);
+    method public void setTargetAspectRatioCustom(android.util.Rational);
+    method public void setTargetRotation(int);
+    method public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
+    method public void takePicture(java.io.File, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    method public void takePicture(java.io.File, androidx.camera.core.ImageCapture.Metadata, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    field public static final int CAPTURE_MODE_MAXIMIZE_QUALITY = 0; // 0x0
+    field public static final int CAPTURE_MODE_MINIMIZE_LATENCY = 1; // 0x1
+    field public static final int ERROR_CAMERA_CLOSED = 3; // 0x3
+    field public static final int ERROR_CAPTURE_FAILED = 2; // 0x2
+    field public static final int ERROR_FILE_IO = 1; // 0x1
+    field public static final int ERROR_INVALID_CAMERA = 4; // 0x4
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int FLASH_MODE_AUTO = 0; // 0x0
+    field public static final int FLASH_MODE_OFF = 2; // 0x2
+    field public static final int FLASH_MODE_ON = 1; // 0x1
+  }
+
+  public static final class ImageCapture.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.ImageCapture> {
+    ctor public ImageCapture.Builder();
+    method public androidx.camera.core.ImageCapture build();
+    method public androidx.camera.core.ImageCapture.Builder setCaptureMode(int);
+    method public androidx.camera.core.ImageCapture.Builder setFlashMode(int);
+    method public androidx.camera.core.ImageCapture.Builder setIoExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageCapture.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.ImageCapture.Builder setTargetName(String);
+    method public androidx.camera.core.ImageCapture.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageCapture.Builder setTargetRotation(int);
+  }
+
+  public static final class ImageCapture.Metadata {
+    ctor public ImageCapture.Metadata();
+    method public android.location.Location? getLocation();
+    method public boolean isReversedHorizontal();
+    method public boolean isReversedVertical();
+    method public void setLocation(android.location.Location?);
+    method public void setReversedHorizontal(boolean);
+    method public void setReversedVertical(boolean);
+  }
+
+  public abstract static class ImageCapture.OnImageCapturedCallback {
+    ctor public ImageCapture.OnImageCapturedCallback();
+    method public void onCaptureSuccess(androidx.camera.core.ImageProxy);
+    method public void onError(int, String, Throwable?);
+  }
+
+  public static interface ImageCapture.OnImageSavedCallback {
+    method public void onError(int, String, Throwable?);
+    method public void onImageSaved(java.io.File);
+  }
+
+  public interface ImageInfo {
+    method public int getRotationDegrees();
+    method public long getTimestamp();
+  }
+
+  public interface ImageProxy extends java.lang.AutoCloseable {
+    method public void close();
+    method public android.graphics.Rect getCropRect();
+    method public int getFormat();
+    method public int getHeight();
+    method public androidx.camera.core.ImageInfo getImageInfo();
+    method public androidx.camera.core.ImageProxy.PlaneProxy![] getPlanes();
+    method public int getWidth();
+    method public void setCropRect(android.graphics.Rect?);
+  }
+
+  public static interface ImageProxy.PlaneProxy {
+    method public java.nio.ByteBuffer getBuffer();
+    method public int getPixelStride();
+    method public int getRowStride();
+  }
+
+  public class MeteringPoint {
+    method public float getSize();
+  }
+
+  public abstract class MeteringPointFactory {
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float);
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float, float);
+    method public static float getDefaultPointSize();
+  }
+
+  public class Preview extends androidx.camera.core.UseCase {
+    method @UiThread public androidx.camera.core.Preview.PreviewSurfaceProvider? getPreviewSurfaceProvider();
+    method @UiThread public void setPreviewSurfaceProvider(java.util.concurrent.Executor, androidx.camera.core.Preview.PreviewSurfaceProvider?);
+    method @UiThread public void setPreviewSurfaceProvider(androidx.camera.core.Preview.PreviewSurfaceProvider?);
+  }
+
+  public static final class Preview.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.Preview> {
+    ctor public Preview.Builder();
+    method public androidx.camera.core.Preview build();
+    method public androidx.camera.core.Preview.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.Preview.Builder setTargetName(String);
+    method public androidx.camera.core.Preview.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.Preview.Builder setTargetRotation(int);
+  }
+
+  public static interface Preview.PreviewSurfaceProvider {
+    method public com.google.common.util.concurrent.ListenableFuture<android.view.Surface!> provideSurface(android.util.Size, com.google.common.util.concurrent.ListenableFuture<java.lang.Void!>);
+  }
+
+  public class SurfaceOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public SurfaceOrientedMeteringPointFactory(float, float);
+    ctor public SurfaceOrientedMeteringPointFactory(float, float, androidx.camera.core.UseCase);
+  }
+
+  public class TorchState {
+    field public static final int OFF = 0; // 0x0
+    field public static final int ON = 1; // 0x1
+  }
+
+  public abstract class UseCase {
+  }
+
+}
+
diff --git a/camera/camera-core/api/public_plus_experimental_1.0.0-alpha08.txt b/camera/camera-core/api/public_plus_experimental_1.0.0-alpha08.txt
new file mode 100644
index 0000000..8b6aaf2
--- /dev/null
+++ b/camera/camera-core/api/public_plus_experimental_1.0.0-alpha08.txt
@@ -0,0 +1,248 @@
+// Signature format: 3.0
+package androidx.camera.core {
+
+  public class AspectRatio {
+    field public static final int RATIO_16_9 = 1; // 0x1
+    field public static final int RATIO_4_3 = 0; // 0x0
+  }
+
+  public interface Camera {
+    method public androidx.camera.core.CameraControl getCameraControl();
+    method public androidx.camera.core.CameraInfo getCameraInfo();
+  }
+
+  public interface CameraControl {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
+  }
+
+  public static final class CameraControl.OperationCanceledException extends java.lang.Exception {
+  }
+
+  public interface CameraInfo {
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getLinearZoom();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getMaxZoomRatio();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getMinZoomRatio();
+    method public int getSensorRotationDegrees();
+    method public int getSensorRotationDegrees(int);
+    method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getZoomRatio();
+    method public boolean hasFlashUnit();
+  }
+
+  public final class CameraInfoUnavailableException extends java.lang.Exception {
+  }
+
+  public final class CameraSelector {
+    field public static final androidx.camera.core.CameraSelector DEFAULT_BACK_CAMERA;
+    field public static final androidx.camera.core.CameraSelector DEFAULT_FRONT_CAMERA;
+    field public static final int LENS_FACING_BACK = 1; // 0x1
+    field public static final int LENS_FACING_FRONT = 0; // 0x0
+  }
+
+  public static final class CameraSelector.Builder {
+    ctor public CameraSelector.Builder();
+    method public androidx.camera.core.CameraSelector build();
+    method public androidx.camera.core.CameraSelector.Builder requireLensFacing(int);
+  }
+
+  public final class CameraXConfig {
+    field public static final androidx.camera.core.impl.Config.Option<java.lang.Class<?>!> OPTION_TARGET_CLASS;
+    field public static final androidx.camera.core.impl.Config.Option<java.lang.String!> OPTION_TARGET_NAME;
+  }
+
+  public static final class CameraXConfig.Builder {
+    ctor public CameraXConfig.Builder();
+    method public androidx.camera.core.CameraXConfig build();
+    method public static androidx.camera.core.CameraXConfig.Builder fromConfig(androidx.camera.core.CameraXConfig);
+    method public androidx.camera.core.CameraXConfig.Builder setCameraExecutor(java.util.concurrent.Executor);
+  }
+
+  public static interface CameraXConfig.Provider {
+    method public androidx.camera.core.CameraXConfig getCameraXConfig();
+  }
+
+  public final class DisplayOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public DisplayOrientedMeteringPointFactory(android.content.Context, androidx.camera.core.CameraSelector, float, float);
+    ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraSelector, float, float);
+  }
+
+  @experimental.Experimental @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalGetImage {
+  }
+
+  public interface ExtendableBuilder<T> {
+    method public T build();
+  }
+
+  public final class FocusMeteringAction {
+    method public long getAutoCancelDurationInMillis();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAe();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAf();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAwb();
+    method public boolean isAutoCancelEnabled();
+    field public static final int FLAG_AE = 2; // 0x2
+    field public static final int FLAG_AF = 1; // 0x1
+    field public static final int FLAG_AWB = 4; // 0x4
+  }
+
+  public static class FocusMeteringAction.Builder {
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint);
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint, int);
+    method public androidx.camera.core.FocusMeteringAction build();
+    method public androidx.camera.core.FocusMeteringAction.Builder disableAutoCancel();
+    method public static androidx.camera.core.FocusMeteringAction.Builder from(androidx.camera.core.MeteringPoint);
+    method public static androidx.camera.core.FocusMeteringAction.Builder from(androidx.camera.core.MeteringPoint, int);
+    method public androidx.camera.core.FocusMeteringAction.Builder setAutoCancelDuration(@IntRange(from=1) long, java.util.concurrent.TimeUnit);
+  }
+
+  public final class FocusMeteringResult {
+    method public boolean isFocusSuccessful();
+  }
+
+  public final class ImageAnalysis extends androidx.camera.core.UseCase {
+    method public void clearAnalyzer();
+    method public void setAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+    method public void setTargetRotation(int);
+    field public static final int STRATEGY_BLOCK_PRODUCER = 1; // 0x1
+    field public static final int STRATEGY_KEEP_ONLY_LATEST = 0; // 0x0
+  }
+
+  public static interface ImageAnalysis.Analyzer {
+    method public void analyze(androidx.camera.core.ImageProxy);
+  }
+
+  public static final class ImageAnalysis.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.ImageAnalysis> {
+    ctor public ImageAnalysis.Builder();
+    method public androidx.camera.core.ImageAnalysis build();
+    method public androidx.camera.core.ImageAnalysis.Builder setBackgroundExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageAnalysis.Builder setBackpressureStrategy(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setImageQueueDepth(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetName(String);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetRotation(int);
+  }
+
+  public class ImageCapture extends androidx.camera.core.UseCase {
+    method public int getFlashMode();
+    method public void setFlashMode(int);
+    method public void setTargetAspectRatioCustom(android.util.Rational);
+    method public void setTargetRotation(int);
+    method public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
+    method public void takePicture(java.io.File, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    method public void takePicture(java.io.File, androidx.camera.core.ImageCapture.Metadata, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    field public static final int CAPTURE_MODE_MAXIMIZE_QUALITY = 0; // 0x0
+    field public static final int CAPTURE_MODE_MINIMIZE_LATENCY = 1; // 0x1
+    field public static final int ERROR_CAMERA_CLOSED = 3; // 0x3
+    field public static final int ERROR_CAPTURE_FAILED = 2; // 0x2
+    field public static final int ERROR_FILE_IO = 1; // 0x1
+    field public static final int ERROR_INVALID_CAMERA = 4; // 0x4
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int FLASH_MODE_AUTO = 0; // 0x0
+    field public static final int FLASH_MODE_OFF = 2; // 0x2
+    field public static final int FLASH_MODE_ON = 1; // 0x1
+  }
+
+  public static final class ImageCapture.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.ImageCapture> {
+    ctor public ImageCapture.Builder();
+    method public androidx.camera.core.ImageCapture build();
+    method public androidx.camera.core.ImageCapture.Builder setCaptureMode(int);
+    method public androidx.camera.core.ImageCapture.Builder setFlashMode(int);
+    method public androidx.camera.core.ImageCapture.Builder setIoExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageCapture.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.ImageCapture.Builder setTargetName(String);
+    method public androidx.camera.core.ImageCapture.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageCapture.Builder setTargetRotation(int);
+  }
+
+  public static final class ImageCapture.Metadata {
+    ctor public ImageCapture.Metadata();
+    method public android.location.Location? getLocation();
+    method public boolean isReversedHorizontal();
+    method public boolean isReversedVertical();
+    method public void setLocation(android.location.Location?);
+    method public void setReversedHorizontal(boolean);
+    method public void setReversedVertical(boolean);
+  }
+
+  public abstract static class ImageCapture.OnImageCapturedCallback {
+    ctor public ImageCapture.OnImageCapturedCallback();
+    method public void onCaptureSuccess(androidx.camera.core.ImageProxy);
+    method public void onError(int, String, Throwable?);
+  }
+
+  public static interface ImageCapture.OnImageSavedCallback {
+    method public void onError(int, String, Throwable?);
+    method public void onImageSaved(java.io.File);
+  }
+
+  public interface ImageInfo {
+    method public int getRotationDegrees();
+    method public long getTimestamp();
+  }
+
+  public interface ImageProxy extends java.lang.AutoCloseable {
+    method public void close();
+    method public android.graphics.Rect getCropRect();
+    method public int getFormat();
+    method public int getHeight();
+    method @androidx.camera.core.ExperimentalGetImage public android.media.Image? getImage();
+    method public androidx.camera.core.ImageInfo getImageInfo();
+    method public androidx.camera.core.ImageProxy.PlaneProxy![] getPlanes();
+    method public int getWidth();
+    method public void setCropRect(android.graphics.Rect?);
+  }
+
+  public static interface ImageProxy.PlaneProxy {
+    method public java.nio.ByteBuffer getBuffer();
+    method public int getPixelStride();
+    method public int getRowStride();
+  }
+
+  public class MeteringPoint {
+    method public float getSize();
+  }
+
+  public abstract class MeteringPointFactory {
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float);
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float, float);
+    method public static float getDefaultPointSize();
+  }
+
+  public class Preview extends androidx.camera.core.UseCase {
+    method @UiThread public androidx.camera.core.Preview.PreviewSurfaceProvider? getPreviewSurfaceProvider();
+    method @UiThread public void setPreviewSurfaceProvider(java.util.concurrent.Executor, androidx.camera.core.Preview.PreviewSurfaceProvider?);
+    method @UiThread public void setPreviewSurfaceProvider(androidx.camera.core.Preview.PreviewSurfaceProvider?);
+  }
+
+  public static final class Preview.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.Preview> {
+    ctor public Preview.Builder();
+    method public androidx.camera.core.Preview build();
+    method public androidx.camera.core.Preview.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.Preview.Builder setTargetName(String);
+    method public androidx.camera.core.Preview.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.Preview.Builder setTargetRotation(int);
+  }
+
+  public static interface Preview.PreviewSurfaceProvider {
+    method public com.google.common.util.concurrent.ListenableFuture<android.view.Surface!> provideSurface(android.util.Size, com.google.common.util.concurrent.ListenableFuture<java.lang.Void!>);
+  }
+
+  public class SurfaceOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public SurfaceOrientedMeteringPointFactory(float, float);
+    ctor public SurfaceOrientedMeteringPointFactory(float, float, androidx.camera.core.UseCase);
+  }
+
+  public class TorchState {
+    field public static final int OFF = 0; // 0x0
+    field public static final int ON = 1; // 0x1
+  }
+
+  public abstract class UseCase {
+  }
+
+}
+
diff --git a/camera/camera-core/api/public_plus_experimental_current.txt b/camera/camera-core/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..8b6aaf2
--- /dev/null
+++ b/camera/camera-core/api/public_plus_experimental_current.txt
@@ -0,0 +1,248 @@
+// Signature format: 3.0
+package androidx.camera.core {
+
+  public class AspectRatio {
+    field public static final int RATIO_16_9 = 1; // 0x1
+    field public static final int RATIO_4_3 = 0; // 0x0
+  }
+
+  public interface Camera {
+    method public androidx.camera.core.CameraControl getCameraControl();
+    method public androidx.camera.core.CameraInfo getCameraInfo();
+  }
+
+  public interface CameraControl {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
+  }
+
+  public static final class CameraControl.OperationCanceledException extends java.lang.Exception {
+  }
+
+  public interface CameraInfo {
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getLinearZoom();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getMaxZoomRatio();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getMinZoomRatio();
+    method public int getSensorRotationDegrees();
+    method public int getSensorRotationDegrees(int);
+    method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getZoomRatio();
+    method public boolean hasFlashUnit();
+  }
+
+  public final class CameraInfoUnavailableException extends java.lang.Exception {
+  }
+
+  public final class CameraSelector {
+    field public static final androidx.camera.core.CameraSelector DEFAULT_BACK_CAMERA;
+    field public static final androidx.camera.core.CameraSelector DEFAULT_FRONT_CAMERA;
+    field public static final int LENS_FACING_BACK = 1; // 0x1
+    field public static final int LENS_FACING_FRONT = 0; // 0x0
+  }
+
+  public static final class CameraSelector.Builder {
+    ctor public CameraSelector.Builder();
+    method public androidx.camera.core.CameraSelector build();
+    method public androidx.camera.core.CameraSelector.Builder requireLensFacing(int);
+  }
+
+  public final class CameraXConfig {
+    field public static final androidx.camera.core.impl.Config.Option<java.lang.Class<?>!> OPTION_TARGET_CLASS;
+    field public static final androidx.camera.core.impl.Config.Option<java.lang.String!> OPTION_TARGET_NAME;
+  }
+
+  public static final class CameraXConfig.Builder {
+    ctor public CameraXConfig.Builder();
+    method public androidx.camera.core.CameraXConfig build();
+    method public static androidx.camera.core.CameraXConfig.Builder fromConfig(androidx.camera.core.CameraXConfig);
+    method public androidx.camera.core.CameraXConfig.Builder setCameraExecutor(java.util.concurrent.Executor);
+  }
+
+  public static interface CameraXConfig.Provider {
+    method public androidx.camera.core.CameraXConfig getCameraXConfig();
+  }
+
+  public final class DisplayOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public DisplayOrientedMeteringPointFactory(android.content.Context, androidx.camera.core.CameraSelector, float, float);
+    ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraSelector, float, float);
+  }
+
+  @experimental.Experimental @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalGetImage {
+  }
+
+  public interface ExtendableBuilder<T> {
+    method public T build();
+  }
+
+  public final class FocusMeteringAction {
+    method public long getAutoCancelDurationInMillis();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAe();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAf();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAwb();
+    method public boolean isAutoCancelEnabled();
+    field public static final int FLAG_AE = 2; // 0x2
+    field public static final int FLAG_AF = 1; // 0x1
+    field public static final int FLAG_AWB = 4; // 0x4
+  }
+
+  public static class FocusMeteringAction.Builder {
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint);
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint, int);
+    method public androidx.camera.core.FocusMeteringAction build();
+    method public androidx.camera.core.FocusMeteringAction.Builder disableAutoCancel();
+    method public static androidx.camera.core.FocusMeteringAction.Builder from(androidx.camera.core.MeteringPoint);
+    method public static androidx.camera.core.FocusMeteringAction.Builder from(androidx.camera.core.MeteringPoint, int);
+    method public androidx.camera.core.FocusMeteringAction.Builder setAutoCancelDuration(@IntRange(from=1) long, java.util.concurrent.TimeUnit);
+  }
+
+  public final class FocusMeteringResult {
+    method public boolean isFocusSuccessful();
+  }
+
+  public final class ImageAnalysis extends androidx.camera.core.UseCase {
+    method public void clearAnalyzer();
+    method public void setAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+    method public void setTargetRotation(int);
+    field public static final int STRATEGY_BLOCK_PRODUCER = 1; // 0x1
+    field public static final int STRATEGY_KEEP_ONLY_LATEST = 0; // 0x0
+  }
+
+  public static interface ImageAnalysis.Analyzer {
+    method public void analyze(androidx.camera.core.ImageProxy);
+  }
+
+  public static final class ImageAnalysis.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.ImageAnalysis> {
+    ctor public ImageAnalysis.Builder();
+    method public androidx.camera.core.ImageAnalysis build();
+    method public androidx.camera.core.ImageAnalysis.Builder setBackgroundExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageAnalysis.Builder setBackpressureStrategy(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setImageQueueDepth(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetName(String);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetRotation(int);
+  }
+
+  public class ImageCapture extends androidx.camera.core.UseCase {
+    method public int getFlashMode();
+    method public void setFlashMode(int);
+    method public void setTargetAspectRatioCustom(android.util.Rational);
+    method public void setTargetRotation(int);
+    method public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
+    method public void takePicture(java.io.File, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    method public void takePicture(java.io.File, androidx.camera.core.ImageCapture.Metadata, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    field public static final int CAPTURE_MODE_MAXIMIZE_QUALITY = 0; // 0x0
+    field public static final int CAPTURE_MODE_MINIMIZE_LATENCY = 1; // 0x1
+    field public static final int ERROR_CAMERA_CLOSED = 3; // 0x3
+    field public static final int ERROR_CAPTURE_FAILED = 2; // 0x2
+    field public static final int ERROR_FILE_IO = 1; // 0x1
+    field public static final int ERROR_INVALID_CAMERA = 4; // 0x4
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int FLASH_MODE_AUTO = 0; // 0x0
+    field public static final int FLASH_MODE_OFF = 2; // 0x2
+    field public static final int FLASH_MODE_ON = 1; // 0x1
+  }
+
+  public static final class ImageCapture.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.ImageCapture> {
+    ctor public ImageCapture.Builder();
+    method public androidx.camera.core.ImageCapture build();
+    method public androidx.camera.core.ImageCapture.Builder setCaptureMode(int);
+    method public androidx.camera.core.ImageCapture.Builder setFlashMode(int);
+    method public androidx.camera.core.ImageCapture.Builder setIoExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageCapture.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.ImageCapture.Builder setTargetName(String);
+    method public androidx.camera.core.ImageCapture.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageCapture.Builder setTargetRotation(int);
+  }
+
+  public static final class ImageCapture.Metadata {
+    ctor public ImageCapture.Metadata();
+    method public android.location.Location? getLocation();
+    method public boolean isReversedHorizontal();
+    method public boolean isReversedVertical();
+    method public void setLocation(android.location.Location?);
+    method public void setReversedHorizontal(boolean);
+    method public void setReversedVertical(boolean);
+  }
+
+  public abstract static class ImageCapture.OnImageCapturedCallback {
+    ctor public ImageCapture.OnImageCapturedCallback();
+    method public void onCaptureSuccess(androidx.camera.core.ImageProxy);
+    method public void onError(int, String, Throwable?);
+  }
+
+  public static interface ImageCapture.OnImageSavedCallback {
+    method public void onError(int, String, Throwable?);
+    method public void onImageSaved(java.io.File);
+  }
+
+  public interface ImageInfo {
+    method public int getRotationDegrees();
+    method public long getTimestamp();
+  }
+
+  public interface ImageProxy extends java.lang.AutoCloseable {
+    method public void close();
+    method public android.graphics.Rect getCropRect();
+    method public int getFormat();
+    method public int getHeight();
+    method @androidx.camera.core.ExperimentalGetImage public android.media.Image? getImage();
+    method public androidx.camera.core.ImageInfo getImageInfo();
+    method public androidx.camera.core.ImageProxy.PlaneProxy![] getPlanes();
+    method public int getWidth();
+    method public void setCropRect(android.graphics.Rect?);
+  }
+
+  public static interface ImageProxy.PlaneProxy {
+    method public java.nio.ByteBuffer getBuffer();
+    method public int getPixelStride();
+    method public int getRowStride();
+  }
+
+  public class MeteringPoint {
+    method public float getSize();
+  }
+
+  public abstract class MeteringPointFactory {
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float);
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float, float);
+    method public static float getDefaultPointSize();
+  }
+
+  public class Preview extends androidx.camera.core.UseCase {
+    method @UiThread public androidx.camera.core.Preview.PreviewSurfaceProvider? getPreviewSurfaceProvider();
+    method @UiThread public void setPreviewSurfaceProvider(java.util.concurrent.Executor, androidx.camera.core.Preview.PreviewSurfaceProvider?);
+    method @UiThread public void setPreviewSurfaceProvider(androidx.camera.core.Preview.PreviewSurfaceProvider?);
+  }
+
+  public static final class Preview.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.Preview> {
+    ctor public Preview.Builder();
+    method public androidx.camera.core.Preview build();
+    method public androidx.camera.core.Preview.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.Preview.Builder setTargetName(String);
+    method public androidx.camera.core.Preview.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.Preview.Builder setTargetRotation(int);
+  }
+
+  public static interface Preview.PreviewSurfaceProvider {
+    method public com.google.common.util.concurrent.ListenableFuture<android.view.Surface!> provideSurface(android.util.Size, com.google.common.util.concurrent.ListenableFuture<java.lang.Void!>);
+  }
+
+  public class SurfaceOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public SurfaceOrientedMeteringPointFactory(float, float);
+    ctor public SurfaceOrientedMeteringPointFactory(float, float, androidx.camera.core.UseCase);
+  }
+
+  public class TorchState {
+    field public static final int OFF = 0; // 0x0
+    field public static final int ON = 1; // 0x1
+  }
+
+  public abstract class UseCase {
+  }
+
+}
+
diff --git a/car-app/app/api/res-1.0.0-alpha01.txt b/camera/camera-core/api/res-1.0.0-alpha08.txt
similarity index 100%
copy from car-app/app/api/res-1.0.0-alpha01.txt
copy to camera/camera-core/api/res-1.0.0-alpha08.txt
diff --git a/camera/camera-core/api/restricted_1.0.0-alpha08.txt b/camera/camera-core/api/restricted_1.0.0-alpha08.txt
new file mode 100644
index 0000000..e2a4ce4
--- /dev/null
+++ b/camera/camera-core/api/restricted_1.0.0-alpha08.txt
@@ -0,0 +1,495 @@
+// Signature format: 3.0
+package androidx.camera.core {
+
+  public class AspectRatio {
+    field public static final int RATIO_16_9 = 1; // 0x1
+    field public static final int RATIO_4_3 = 0; // 0x0
+  }
+
+  @IntDef({androidx.camera.core.AspectRatio.RATIO_4_3, androidx.camera.core.AspectRatio.RATIO_16_9}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface AspectRatio.Ratio {
+  }
+
+  public interface Camera {
+    method public androidx.camera.core.CameraControl getCameraControl();
+    method public androidx.camera.core.CameraInfo getCameraInfo();
+  }
+
+  public interface CameraControl {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
+  }
+
+  public static final class CameraControl.OperationCanceledException extends java.lang.Exception {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public CameraControl.OperationCanceledException(String);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public CameraControl.OperationCanceledException(String, Throwable);
+  }
+
+  public interface CameraInfo {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.camera.core.CameraInfo.ImplementationType public String getImplementationType();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getLinearZoom();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getMaxZoomRatio();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getMinZoomRatio();
+    method public int getSensorRotationDegrees();
+    method public int getSensorRotationDegrees(int);
+    method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getZoomRatio();
+    method public boolean hasFlashUnit();
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final String IMPLEMENTATION_TYPE_CAMERA2 = "androidx.camera.camera2";
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final String IMPLEMENTATION_TYPE_CAMERA2_LEGACY = "androidx.camera.camera2.legacy";
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final String IMPLEMENTATION_TYPE_FAKE = "androidx.camera.fake";
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final String IMPLEMENTATION_TYPE_UNKNOWN = "<unknown>";
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @StringDef(open=true, value={androidx.camera.core.CameraInfo.IMPLEMENTATION_TYPE_UNKNOWN, androidx.camera.core.CameraInfo.IMPLEMENTATION_TYPE_CAMERA2_LEGACY, androidx.camera.core.CameraInfo.IMPLEMENTATION_TYPE_CAMERA2, androidx.camera.core.CameraInfo.IMPLEMENTATION_TYPE_FAKE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface CameraInfo.ImplementationType {
+  }
+
+  public final class CameraInfoUnavailableException extends java.lang.Exception {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public CameraInfoUnavailableException(String!, Throwable!);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public CameraInfoUnavailableException(String!);
+  }
+
+  public final class CameraSelector {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public java.util.LinkedHashSet<androidx.camera.core.impl.CameraIdFilter!> getCameraFilterSet();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public Integer? getLensFacing();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public String select(java.util.Set<java.lang.String!>);
+    field public static final androidx.camera.core.CameraSelector DEFAULT_BACK_CAMERA;
+    field public static final androidx.camera.core.CameraSelector DEFAULT_FRONT_CAMERA;
+    field public static final int LENS_FACING_BACK = 1; // 0x1
+    field public static final int LENS_FACING_FRONT = 0; // 0x0
+  }
+
+  public static final class CameraSelector.Builder {
+    ctor public CameraSelector.Builder();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.CameraSelector.Builder appendFilter(androidx.camera.core.impl.CameraIdFilter);
+    method public androidx.camera.core.CameraSelector build();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.CameraSelector.Builder fromSelector(androidx.camera.core.CameraSelector);
+    method public androidx.camera.core.CameraSelector.Builder requireLensFacing(@androidx.camera.core.CameraSelector.LensFacing int);
+  }
+
+  @IntDef({androidx.camera.core.CameraSelector.LENS_FACING_FRONT, androidx.camera.core.CameraSelector.LENS_FACING_BACK}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface CameraSelector.LensFacing {
+  }
+
+  @MainThread @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class CameraX {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.Camera bindToLifecycle(androidx.lifecycle.LifecycleOwner, androidx.camera.core.CameraSelector, androidx.camera.core.UseCase!...);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static java.util.Collection<androidx.camera.core.UseCase!>? getActiveUseCases();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.impl.CameraFactory getCameraFactory();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.impl.CameraInfoInternal getCameraInfo(String!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static String? getCameraWithCameraSelector(androidx.camera.core.CameraSelector);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static String? getCameraWithLensFacing(@androidx.camera.core.CameraSelector.LensFacing int) throws androidx.camera.core.CameraInfoUnavailableException;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static android.content.Context getContext();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.camera.core.CameraSelector.LensFacing public static int getDefaultLensFacing() throws androidx.camera.core.CameraInfoUnavailableException;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <C extends androidx.camera.core.impl.UseCaseConfig<?>> C? getDefaultUseCaseConfig(Class<C!>!, androidx.camera.core.CameraInfo?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.CameraX!> getOrCreateInstance(android.content.Context);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.impl.CameraDeviceSurfaceManager getSurfaceManager();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public static com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> initialize(android.content.Context, androidx.camera.core.CameraXConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static boolean isBound(androidx.camera.core.UseCase);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static boolean isInitialized();
+    method public static com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> shutdown();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static void unbind(androidx.camera.core.UseCase!...);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static void unbindAll();
+  }
+
+  public final class CameraXConfig implements androidx.camera.core.impl.Config {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public boolean containsOption(androidx.camera.core.impl.Config.Option<?>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void findOptions(String, androidx.camera.core.impl.Config.OptionMatcher);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public java.util.concurrent.Executor? getCameraExecutor(java.util.concurrent.Executor?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.CameraFactory.Provider? getCameraFactoryProvider(androidx.camera.core.impl.CameraFactory.Provider?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.CameraDeviceSurfaceManager.Provider? getDeviceSurfaceManagerProvider(androidx.camera.core.impl.CameraDeviceSurfaceManager.Provider?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public Class<androidx.camera.core.CameraX!>? getTargetClass(Class<androidx.camera.core.CameraX!>?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public Class<androidx.camera.core.CameraX!> getTargetClass();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public String? getTargetName(String?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public String getTargetName();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.UseCaseConfigFactory.Provider? getUseCaseConfigFactoryProvider(androidx.camera.core.impl.UseCaseConfigFactory.Provider?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public java.util.Set<androidx.camera.core.impl.Config.Option<?>!> listOptions();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public <ValueT> ValueT? retrieveOption(androidx.camera.core.impl.Config.Option<ValueT!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public <ValueT> ValueT? retrieveOption(androidx.camera.core.impl.Config.Option<ValueT!>, ValueT?);
+    field public static final androidx.camera.core.impl.Config.Option<java.lang.Class<?>!> OPTION_TARGET_CLASS;
+    field public static final androidx.camera.core.impl.Config.Option<java.lang.String!> OPTION_TARGET_NAME;
+  }
+
+  public static final class CameraXConfig.Builder {
+    ctor public CameraXConfig.Builder();
+    method public androidx.camera.core.CameraXConfig build();
+    method public static androidx.camera.core.CameraXConfig.Builder fromConfig(androidx.camera.core.CameraXConfig);
+    method public androidx.camera.core.CameraXConfig.Builder setCameraExecutor(java.util.concurrent.Executor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.CameraXConfig.Builder setCameraFactoryProvider(androidx.camera.core.impl.CameraFactory.Provider);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.CameraXConfig.Builder setDeviceSurfaceManagerProvider(androidx.camera.core.impl.CameraDeviceSurfaceManager.Provider);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.CameraXConfig.Builder setTargetClass(Class<androidx.camera.core.CameraX!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.CameraXConfig.Builder setTargetName(String);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.CameraXConfig.Builder setUseCaseConfigFactoryProvider(androidx.camera.core.impl.UseCaseConfigFactory.Provider);
+  }
+
+  public static interface CameraXConfig.Provider {
+    method public androidx.camera.core.CameraXConfig getCameraXConfig();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class CameraXThreads {
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final String TAG = "CameraX-";
+  }
+
+  public final class DisplayOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public DisplayOrientedMeteringPointFactory(android.content.Context, androidx.camera.core.CameraSelector, float, float);
+    ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraSelector, float, float);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected android.graphics.PointF convertPoint(float, float);
+  }
+
+  public interface ExtendableBuilder<T> {
+    method public T build();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.MutableConfig getMutableConfig();
+  }
+
+  public final class FocusMeteringAction {
+    method public long getAutoCancelDurationInMillis();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAe();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAf();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAwb();
+    method public boolean isAutoCancelEnabled();
+    field public static final int FLAG_AE = 2; // 0x2
+    field public static final int FLAG_AF = 1; // 0x1
+    field public static final int FLAG_AWB = 4; // 0x4
+  }
+
+  public static class FocusMeteringAction.Builder {
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint);
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint, @androidx.camera.core.FocusMeteringAction.MeteringMode int);
+    method public androidx.camera.core.FocusMeteringAction build();
+    method public androidx.camera.core.FocusMeteringAction.Builder disableAutoCancel();
+    method public static androidx.camera.core.FocusMeteringAction.Builder from(androidx.camera.core.MeteringPoint);
+    method public static androidx.camera.core.FocusMeteringAction.Builder from(androidx.camera.core.MeteringPoint, @androidx.camera.core.FocusMeteringAction.MeteringMode int);
+    method public androidx.camera.core.FocusMeteringAction.Builder setAutoCancelDuration(@IntRange(from=1) long, java.util.concurrent.TimeUnit);
+  }
+
+  @IntDef(flag=true, value={androidx.camera.core.FocusMeteringAction.FLAG_AF, androidx.camera.core.FocusMeteringAction.FLAG_AE, androidx.camera.core.FocusMeteringAction.FLAG_AWB}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface FocusMeteringAction.MeteringMode {
+  }
+
+  public final class FocusMeteringResult {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.FocusMeteringResult create(boolean);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.FocusMeteringResult emptyInstance();
+    method public boolean isFocusSuccessful();
+  }
+
+  public final class ImageAnalysis extends androidx.camera.core.UseCase {
+    method public void clearAnalyzer();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected java.util.Map<java.lang.String!,android.util.Size!> onSuggestedResolutionUpdated(java.util.Map<java.lang.String!,android.util.Size!>);
+    method public void setAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+    method public void setTargetRotation(int);
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final androidx.camera.core.ImageAnalysis.Defaults! DEFAULT_CONFIG;
+    field public static final int STRATEGY_BLOCK_PRODUCER = 1; // 0x1
+    field public static final int STRATEGY_KEEP_ONLY_LATEST = 0; // 0x0
+  }
+
+  public static interface ImageAnalysis.Analyzer {
+    method public void analyze(androidx.camera.core.ImageProxy);
+  }
+
+  @IntDef({androidx.camera.core.ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST, androidx.camera.core.ImageAnalysis.STRATEGY_BLOCK_PRODUCER}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ImageAnalysis.BackpressureStrategy {
+  }
+
+  public static final class ImageAnalysis.Builder implements androidx.camera.core.impl.UseCaseConfig.Builder<androidx.camera.core.ImageAnalysis,androidx.camera.core.impl.ImageAnalysisConfig,androidx.camera.core.ImageAnalysis.Builder> {
+    ctor public ImageAnalysis.Builder();
+    method public androidx.camera.core.ImageAnalysis build();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.ImageAnalysis.Builder fromConfig(androidx.camera.core.impl.ImageAnalysisConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.MutableConfig getMutableConfig();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.ImageAnalysisConfig getUseCaseConfig();
+    method public androidx.camera.core.ImageAnalysis.Builder setBackgroundExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageAnalysis.Builder setBackpressureStrategy(@androidx.camera.core.ImageAnalysis.BackpressureStrategy int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setCaptureOptionUnpacker(androidx.camera.core.impl.CaptureConfig.OptionUnpacker);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setDefaultCaptureConfig(androidx.camera.core.impl.CaptureConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setDefaultResolution(android.util.Size);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setDefaultSessionConfig(androidx.camera.core.impl.SessionConfig);
+    method public androidx.camera.core.ImageAnalysis.Builder setImageQueueDepth(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setMaxResolution(android.util.Size);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setSessionOptionUnpacker(androidx.camera.core.impl.SessionConfig.OptionUnpacker);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setSupportedResolutions(java.util.List<android.util.Pair<java.lang.Integer!,android.util.Size![]!>!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setSurfaceOccupancyPriority(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetAspectRatio(@androidx.camera.core.AspectRatio.Ratio int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setTargetAspectRatioCustom(android.util.Rational);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setTargetClass(Class<androidx.camera.core.ImageAnalysis!>);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetName(String);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetRotation(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setUseCaseEventCallback(androidx.camera.core.UseCase.EventCallback);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final class ImageAnalysis.Defaults {
+    ctor public ImageAnalysis.Defaults();
+    method public androidx.camera.core.impl.ImageAnalysisConfig getConfig(androidx.camera.core.CameraInfo?);
+  }
+
+  public class ImageCapture extends androidx.camera.core.UseCase {
+    method @androidx.camera.core.ImageCapture.FlashMode public int getFlashMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected java.util.Map<java.lang.String!,android.util.Size!> onSuggestedResolutionUpdated(java.util.Map<java.lang.String!,android.util.Size!>);
+    method public void setFlashMode(@androidx.camera.core.ImageCapture.FlashMode int);
+    method public void setTargetAspectRatioCustom(android.util.Rational);
+    method public void setTargetRotation(int);
+    method public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
+    method public void takePicture(java.io.File, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    method public void takePicture(java.io.File, androidx.camera.core.ImageCapture.Metadata, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    field public static final int CAPTURE_MODE_MAXIMIZE_QUALITY = 0; // 0x0
+    field public static final int CAPTURE_MODE_MINIMIZE_LATENCY = 1; // 0x1
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final androidx.camera.core.ImageCapture.Defaults! DEFAULT_CONFIG;
+    field public static final int ERROR_CAMERA_CLOSED = 3; // 0x3
+    field public static final int ERROR_CAPTURE_FAILED = 2; // 0x2
+    field public static final int ERROR_FILE_IO = 1; // 0x1
+    field public static final int ERROR_INVALID_CAMERA = 4; // 0x4
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int FLASH_MODE_AUTO = 0; // 0x0
+    field public static final int FLASH_MODE_OFF = 2; // 0x2
+    field public static final int FLASH_MODE_ON = 1; // 0x1
+  }
+
+  public static final class ImageCapture.Builder implements androidx.camera.core.impl.UseCaseConfig.Builder<androidx.camera.core.ImageCapture,androidx.camera.core.impl.ImageCaptureConfig,androidx.camera.core.ImageCapture.Builder> {
+    ctor public ImageCapture.Builder();
+    method public androidx.camera.core.ImageCapture build();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.ImageCapture.Builder fromConfig(androidx.camera.core.impl.ImageCaptureConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.MutableConfig getMutableConfig();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.ImageCaptureConfig getUseCaseConfig();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setBufferFormat(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setCameraSelector(androidx.camera.core.CameraSelector);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setCaptureBundle(androidx.camera.core.impl.CaptureBundle);
+    method public androidx.camera.core.ImageCapture.Builder setCaptureMode(@androidx.camera.core.ImageCapture.CaptureMode int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setCaptureOptionUnpacker(androidx.camera.core.impl.CaptureConfig.OptionUnpacker);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setCaptureProcessor(androidx.camera.core.impl.CaptureProcessor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setDefaultCaptureConfig(androidx.camera.core.impl.CaptureConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setDefaultResolution(android.util.Size);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setDefaultSessionConfig(androidx.camera.core.impl.SessionConfig);
+    method public androidx.camera.core.ImageCapture.Builder setFlashMode(@androidx.camera.core.ImageCapture.FlashMode int);
+    method public androidx.camera.core.ImageCapture.Builder setIoExecutor(java.util.concurrent.Executor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setMaxCaptureStages(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setMaxResolution(android.util.Size);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setSessionOptionUnpacker(androidx.camera.core.impl.SessionConfig.OptionUnpacker);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setSupportedResolutions(java.util.List<android.util.Pair<java.lang.Integer!,android.util.Size![]!>!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setSurfaceOccupancyPriority(int);
+    method public androidx.camera.core.ImageCapture.Builder setTargetAspectRatio(@androidx.camera.core.AspectRatio.Ratio int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setTargetAspectRatioCustom(android.util.Rational);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setTargetClass(Class<androidx.camera.core.ImageCapture!>);
+    method public androidx.camera.core.ImageCapture.Builder setTargetName(String);
+    method public androidx.camera.core.ImageCapture.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageCapture.Builder setTargetRotation(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setUseCaseEventCallback(androidx.camera.core.UseCase.EventCallback);
+  }
+
+  @IntDef({androidx.camera.core.ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY, androidx.camera.core.ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ImageCapture.CaptureMode {
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final class ImageCapture.Defaults {
+    ctor public ImageCapture.Defaults();
+    method public androidx.camera.core.impl.ImageCaptureConfig getConfig(androidx.camera.core.CameraInfo?);
+  }
+
+  @IntDef({androidx.camera.core.ImageCapture.FLASH_MODE_AUTO, androidx.camera.core.ImageCapture.FLASH_MODE_ON, androidx.camera.core.ImageCapture.FLASH_MODE_OFF}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ImageCapture.FlashMode {
+  }
+
+  @IntDef({androidx.camera.core.ImageCapture.ERROR_UNKNOWN, androidx.camera.core.ImageCapture.ERROR_FILE_IO, androidx.camera.core.ImageCapture.ERROR_CAPTURE_FAILED, androidx.camera.core.ImageCapture.ERROR_CAMERA_CLOSED, androidx.camera.core.ImageCapture.ERROR_INVALID_CAMERA}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ImageCapture.ImageCaptureError {
+  }
+
+  public static final class ImageCapture.Metadata {
+    ctor public ImageCapture.Metadata();
+    method public android.location.Location? getLocation();
+    method public boolean isReversedHorizontal();
+    method public boolean isReversedVertical();
+    method public void setLocation(android.location.Location?);
+    method public void setReversedHorizontal(boolean);
+    method public void setReversedVertical(boolean);
+  }
+
+  public abstract static class ImageCapture.OnImageCapturedCallback {
+    ctor public ImageCapture.OnImageCapturedCallback();
+    method public void onCaptureSuccess(androidx.camera.core.ImageProxy);
+    method public void onError(@androidx.camera.core.ImageCapture.ImageCaptureError int, String, Throwable?);
+  }
+
+  public static interface ImageCapture.OnImageSavedCallback {
+    method public void onError(@androidx.camera.core.ImageCapture.ImageCaptureError int, String, Throwable?);
+    method public void onImageSaved(java.io.File);
+  }
+
+  public interface ImageInfo {
+    method public int getRotationDegrees();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public Object? getTag();
+    method public long getTimestamp();
+  }
+
+  public interface ImageProxy extends java.lang.AutoCloseable {
+    method public void close();
+    method public android.graphics.Rect getCropRect();
+    method public int getFormat();
+    method public int getHeight();
+    method public androidx.camera.core.ImageInfo getImageInfo();
+    method public androidx.camera.core.ImageProxy.PlaneProxy![] getPlanes();
+    method public int getWidth();
+    method public void setCropRect(android.graphics.Rect?);
+  }
+
+  public static interface ImageProxy.PlaneProxy {
+    method public java.nio.ByteBuffer getBuffer();
+    method public int getPixelStride();
+    method public int getRowStride();
+  }
+
+  public class MeteringPoint {
+    method public float getSize();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public android.util.Rational? getSurfaceAspectRatio();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public float getX();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public float getY();
+  }
+
+  public abstract class MeteringPointFactory {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public MeteringPointFactory();
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public MeteringPointFactory(android.util.Rational?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected abstract android.graphics.PointF convertPoint(float, float);
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float);
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float, float);
+    method public static float getDefaultPointSize();
+  }
+
+  public class Preview extends androidx.camera.core.UseCase {
+    method @UiThread public androidx.camera.core.Preview.PreviewSurfaceProvider? getPreviewSurfaceProvider();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected java.util.Map<java.lang.String!,android.util.Size!> onSuggestedResolutionUpdated(java.util.Map<java.lang.String!,android.util.Size!>);
+    method @UiThread public void setPreviewSurfaceProvider(java.util.concurrent.Executor, androidx.camera.core.Preview.PreviewSurfaceProvider?);
+    method @UiThread public void setPreviewSurfaceProvider(androidx.camera.core.Preview.PreviewSurfaceProvider?);
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final androidx.camera.core.Preview.Defaults! DEFAULT_CONFIG;
+  }
+
+  public static final class Preview.Builder implements androidx.camera.core.impl.UseCaseConfig.Builder<androidx.camera.core.Preview,androidx.camera.core.impl.PreviewConfig,androidx.camera.core.Preview.Builder> {
+    ctor public Preview.Builder();
+    method public androidx.camera.core.Preview build();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.Preview.Builder fromConfig(androidx.camera.core.impl.PreviewConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.MutableConfig getMutableConfig();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.PreviewConfig getUseCaseConfig();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setBackgroundExecutor(java.util.concurrent.Executor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setCameraSelector(androidx.camera.core.CameraSelector);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setCaptureOptionUnpacker(androidx.camera.core.impl.CaptureConfig.OptionUnpacker);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setCaptureProcessor(androidx.camera.core.impl.CaptureProcessor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setDefaultCaptureConfig(androidx.camera.core.impl.CaptureConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setDefaultResolution(android.util.Size);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setDefaultSessionConfig(androidx.camera.core.impl.SessionConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setImageInfoProcessor(androidx.camera.core.impl.ImageInfoProcessor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setMaxResolution(android.util.Size);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setSessionOptionUnpacker(androidx.camera.core.impl.SessionConfig.OptionUnpacker);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setSupportedResolutions(java.util.List<android.util.Pair<java.lang.Integer!,android.util.Size![]!>!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setSurfaceOccupancyPriority(int);
+    method public androidx.camera.core.Preview.Builder setTargetAspectRatio(@androidx.camera.core.AspectRatio.Ratio int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setTargetAspectRatioCustom(android.util.Rational);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setTargetClass(Class<androidx.camera.core.Preview!>);
+    method public androidx.camera.core.Preview.Builder setTargetName(String);
+    method public androidx.camera.core.Preview.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.Preview.Builder setTargetRotation(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setUseCaseEventCallback(androidx.camera.core.UseCase.EventCallback);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final class Preview.Defaults {
+    ctor public Preview.Defaults();
+    method public androidx.camera.core.impl.PreviewConfig getConfig(androidx.camera.core.CameraInfo?);
+  }
+
+  public static interface Preview.PreviewSurfaceProvider {
+    method public com.google.common.util.concurrent.ListenableFuture<android.view.Surface!> provideSurface(android.util.Size, com.google.common.util.concurrent.ListenableFuture<java.lang.Void!>);
+  }
+
+  public class SurfaceOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public SurfaceOrientedMeteringPointFactory(float, float);
+    ctor public SurfaceOrientedMeteringPointFactory(float, float, androidx.camera.core.UseCase);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected android.graphics.PointF convertPoint(float, float);
+  }
+
+  public class TorchState {
+    field public static final int OFF = 0; // 0x0
+    field public static final int ON = 1; // 0x1
+  }
+
+  @IntDef({androidx.camera.core.TorchState.OFF, androidx.camera.core.TorchState.ON}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface TorchState.State {
+  }
+
+  public abstract class UseCase {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected UseCase(androidx.camera.core.impl.UseCaseConfig<?>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void addStateChangeCallback(androidx.camera.core.UseCase.StateChangeCallback);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected androidx.camera.core.impl.UseCaseConfig<?> applyDefaults(androidx.camera.core.impl.UseCaseConfig<?>, androidx.camera.core.impl.UseCaseConfig.Builder<?,?,?>?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final void attachCameraControl(String!, androidx.camera.core.impl.CameraControlInternal!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected void attachToCamera(String!, androidx.camera.core.impl.SessionConfig!);
+    method @CallSuper @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void clear();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public java.util.Set<java.lang.String!>! getAttachedCameraIds();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public android.util.Size! getAttachedSurfaceResolution(String!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.CameraInternal? getBoundCamera();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected String getBoundCameraId();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected androidx.camera.core.impl.CameraControlInternal! getCameraControl(String!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected androidx.camera.core.impl.UseCaseConfig.Builder<?,?,?>? getDefaultBuilder(androidx.camera.core.CameraInfo?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public int getImageFormat();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public String getName();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.SessionConfig! getSessionConfig(String!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.UseCaseConfig<?>! getUseCaseConfig();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected boolean isCurrentlyBoundCamera(String);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final void notifyActive();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final void notifyInactive();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final void notifyReset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final void notifyState();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final void notifyUpdated();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected void onBind(androidx.camera.core.impl.CameraInternal);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected void onCameraControlReady(String!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void onStateOffline(String);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void onStateOnline(String);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected abstract java.util.Map<java.lang.String!,android.util.Size!> onSuggestedResolutionUpdated(java.util.Map<java.lang.String!,android.util.Size!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void removeStateChangeCallback(androidx.camera.core.UseCase.StateChangeCallback);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected void setImageFormat(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void updateSuggestedResolution(java.util.Map<java.lang.String!,android.util.Size!>!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final void updateUseCaseConfig(androidx.camera.core.impl.UseCaseConfig<?>);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static interface UseCase.EventCallback {
+    method public void onBind(String);
+    method public void onUnbind();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static interface UseCase.StateChangeCallback {
+    method public void onUseCaseActive(androidx.camera.core.UseCase);
+    method public void onUseCaseInactive(androidx.camera.core.UseCase);
+    method public void onUseCaseReset(androidx.camera.core.UseCase);
+    method public void onUseCaseUpdated(androidx.camera.core.UseCase);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class VideoCapture extends androidx.camera.core.UseCase {
+    ctor public VideoCapture(androidx.camera.core.impl.VideoCaptureConfig!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected java.util.Map<java.lang.String!,android.util.Size!> onSuggestedResolutionUpdated(java.util.Map<java.lang.String!,android.util.Size!>);
+    method public void setTargetRotation(int);
+    method public void startRecording(java.io.File, java.util.concurrent.Executor, androidx.camera.core.VideoCapture.OnVideoSavedCallback);
+    method public void startRecording(java.io.File, androidx.camera.core.VideoCapture.Metadata, java.util.concurrent.Executor, androidx.camera.core.VideoCapture.OnVideoSavedCallback);
+    method public void stopRecording();
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final androidx.camera.core.VideoCapture.Defaults! DEFAULT_CONFIG;
+    field public static final int ERROR_ENCODER = 1; // 0x1
+    field public static final int ERROR_MUXER = 2; // 0x2
+    field public static final int ERROR_RECORDING_IN_PROGRESS = 3; // 0x3
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final class VideoCapture.Defaults {
+    ctor public VideoCapture.Defaults();
+    method public androidx.camera.core.impl.VideoCaptureConfig getConfig(androidx.camera.core.CameraInfo?);
+  }
+
+  public static final class VideoCapture.Metadata {
+    ctor public VideoCapture.Metadata();
+    field public android.location.Location? location;
+  }
+
+  public static interface VideoCapture.OnVideoSavedCallback {
+    method public void onError(@androidx.camera.core.VideoCapture.VideoCaptureError int, String, Throwable?);
+    method public void onVideoSaved(java.io.File);
+  }
+
+  @IntDef({androidx.camera.core.VideoCapture.ERROR_UNKNOWN, androidx.camera.core.VideoCapture.ERROR_ENCODER, androidx.camera.core.VideoCapture.ERROR_MUXER, androidx.camera.core.VideoCapture.ERROR_RECORDING_IN_PROGRESS}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface VideoCapture.VideoCaptureError {
+  }
+
+}
+
+package @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) androidx.camera.core.impl.annotation {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface ExecutedBy {
+    method public abstract String value();
+  }
+
+}
+
diff --git a/camera/camera-core/api/restricted_current.txt b/camera/camera-core/api/restricted_current.txt
new file mode 100644
index 0000000..e2a4ce4
--- /dev/null
+++ b/camera/camera-core/api/restricted_current.txt
@@ -0,0 +1,495 @@
+// Signature format: 3.0
+package androidx.camera.core {
+
+  public class AspectRatio {
+    field public static final int RATIO_16_9 = 1; // 0x1
+    field public static final int RATIO_4_3 = 0; // 0x0
+  }
+
+  @IntDef({androidx.camera.core.AspectRatio.RATIO_4_3, androidx.camera.core.AspectRatio.RATIO_16_9}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface AspectRatio.Ratio {
+  }
+
+  public interface Camera {
+    method public androidx.camera.core.CameraControl getCameraControl();
+    method public androidx.camera.core.CameraInfo getCameraInfo();
+  }
+
+  public interface CameraControl {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
+  }
+
+  public static final class CameraControl.OperationCanceledException extends java.lang.Exception {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public CameraControl.OperationCanceledException(String);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public CameraControl.OperationCanceledException(String, Throwable);
+  }
+
+  public interface CameraInfo {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.camera.core.CameraInfo.ImplementationType public String getImplementationType();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getLinearZoom();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getMaxZoomRatio();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getMinZoomRatio();
+    method public int getSensorRotationDegrees();
+    method public int getSensorRotationDegrees(int);
+    method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+    method public androidx.lifecycle.LiveData<java.lang.Float!> getZoomRatio();
+    method public boolean hasFlashUnit();
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final String IMPLEMENTATION_TYPE_CAMERA2 = "androidx.camera.camera2";
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final String IMPLEMENTATION_TYPE_CAMERA2_LEGACY = "androidx.camera.camera2.legacy";
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final String IMPLEMENTATION_TYPE_FAKE = "androidx.camera.fake";
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final String IMPLEMENTATION_TYPE_UNKNOWN = "<unknown>";
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @StringDef(open=true, value={androidx.camera.core.CameraInfo.IMPLEMENTATION_TYPE_UNKNOWN, androidx.camera.core.CameraInfo.IMPLEMENTATION_TYPE_CAMERA2_LEGACY, androidx.camera.core.CameraInfo.IMPLEMENTATION_TYPE_CAMERA2, androidx.camera.core.CameraInfo.IMPLEMENTATION_TYPE_FAKE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface CameraInfo.ImplementationType {
+  }
+
+  public final class CameraInfoUnavailableException extends java.lang.Exception {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public CameraInfoUnavailableException(String!, Throwable!);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public CameraInfoUnavailableException(String!);
+  }
+
+  public final class CameraSelector {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public java.util.LinkedHashSet<androidx.camera.core.impl.CameraIdFilter!> getCameraFilterSet();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public Integer? getLensFacing();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public String select(java.util.Set<java.lang.String!>);
+    field public static final androidx.camera.core.CameraSelector DEFAULT_BACK_CAMERA;
+    field public static final androidx.camera.core.CameraSelector DEFAULT_FRONT_CAMERA;
+    field public static final int LENS_FACING_BACK = 1; // 0x1
+    field public static final int LENS_FACING_FRONT = 0; // 0x0
+  }
+
+  public static final class CameraSelector.Builder {
+    ctor public CameraSelector.Builder();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.CameraSelector.Builder appendFilter(androidx.camera.core.impl.CameraIdFilter);
+    method public androidx.camera.core.CameraSelector build();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.CameraSelector.Builder fromSelector(androidx.camera.core.CameraSelector);
+    method public androidx.camera.core.CameraSelector.Builder requireLensFacing(@androidx.camera.core.CameraSelector.LensFacing int);
+  }
+
+  @IntDef({androidx.camera.core.CameraSelector.LENS_FACING_FRONT, androidx.camera.core.CameraSelector.LENS_FACING_BACK}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface CameraSelector.LensFacing {
+  }
+
+  @MainThread @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class CameraX {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.Camera bindToLifecycle(androidx.lifecycle.LifecycleOwner, androidx.camera.core.CameraSelector, androidx.camera.core.UseCase!...);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static java.util.Collection<androidx.camera.core.UseCase!>? getActiveUseCases();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.impl.CameraFactory getCameraFactory();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.impl.CameraInfoInternal getCameraInfo(String!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static String? getCameraWithCameraSelector(androidx.camera.core.CameraSelector);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static String? getCameraWithLensFacing(@androidx.camera.core.CameraSelector.LensFacing int) throws androidx.camera.core.CameraInfoUnavailableException;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static android.content.Context getContext();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.camera.core.CameraSelector.LensFacing public static int getDefaultLensFacing() throws androidx.camera.core.CameraInfoUnavailableException;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <C extends androidx.camera.core.impl.UseCaseConfig<?>> C? getDefaultUseCaseConfig(Class<C!>!, androidx.camera.core.CameraInfo?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.CameraX!> getOrCreateInstance(android.content.Context);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.impl.CameraDeviceSurfaceManager getSurfaceManager();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public static com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> initialize(android.content.Context, androidx.camera.core.CameraXConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static boolean isBound(androidx.camera.core.UseCase);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static boolean isInitialized();
+    method public static com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> shutdown();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static void unbind(androidx.camera.core.UseCase!...);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static void unbindAll();
+  }
+
+  public final class CameraXConfig implements androidx.camera.core.impl.Config {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public boolean containsOption(androidx.camera.core.impl.Config.Option<?>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void findOptions(String, androidx.camera.core.impl.Config.OptionMatcher);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public java.util.concurrent.Executor? getCameraExecutor(java.util.concurrent.Executor?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.CameraFactory.Provider? getCameraFactoryProvider(androidx.camera.core.impl.CameraFactory.Provider?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.CameraDeviceSurfaceManager.Provider? getDeviceSurfaceManagerProvider(androidx.camera.core.impl.CameraDeviceSurfaceManager.Provider?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public Class<androidx.camera.core.CameraX!>? getTargetClass(Class<androidx.camera.core.CameraX!>?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public Class<androidx.camera.core.CameraX!> getTargetClass();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public String? getTargetName(String?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public String getTargetName();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.UseCaseConfigFactory.Provider? getUseCaseConfigFactoryProvider(androidx.camera.core.impl.UseCaseConfigFactory.Provider?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public java.util.Set<androidx.camera.core.impl.Config.Option<?>!> listOptions();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public <ValueT> ValueT? retrieveOption(androidx.camera.core.impl.Config.Option<ValueT!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public <ValueT> ValueT? retrieveOption(androidx.camera.core.impl.Config.Option<ValueT!>, ValueT?);
+    field public static final androidx.camera.core.impl.Config.Option<java.lang.Class<?>!> OPTION_TARGET_CLASS;
+    field public static final androidx.camera.core.impl.Config.Option<java.lang.String!> OPTION_TARGET_NAME;
+  }
+
+  public static final class CameraXConfig.Builder {
+    ctor public CameraXConfig.Builder();
+    method public androidx.camera.core.CameraXConfig build();
+    method public static androidx.camera.core.CameraXConfig.Builder fromConfig(androidx.camera.core.CameraXConfig);
+    method public androidx.camera.core.CameraXConfig.Builder setCameraExecutor(java.util.concurrent.Executor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.CameraXConfig.Builder setCameraFactoryProvider(androidx.camera.core.impl.CameraFactory.Provider);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.CameraXConfig.Builder setDeviceSurfaceManagerProvider(androidx.camera.core.impl.CameraDeviceSurfaceManager.Provider);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.CameraXConfig.Builder setTargetClass(Class<androidx.camera.core.CameraX!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.CameraXConfig.Builder setTargetName(String);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.CameraXConfig.Builder setUseCaseConfigFactoryProvider(androidx.camera.core.impl.UseCaseConfigFactory.Provider);
+  }
+
+  public static interface CameraXConfig.Provider {
+    method public androidx.camera.core.CameraXConfig getCameraXConfig();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class CameraXThreads {
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final String TAG = "CameraX-";
+  }
+
+  public final class DisplayOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public DisplayOrientedMeteringPointFactory(android.content.Context, androidx.camera.core.CameraSelector, float, float);
+    ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraSelector, float, float);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected android.graphics.PointF convertPoint(float, float);
+  }
+
+  public interface ExtendableBuilder<T> {
+    method public T build();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.MutableConfig getMutableConfig();
+  }
+
+  public final class FocusMeteringAction {
+    method public long getAutoCancelDurationInMillis();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAe();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAf();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAwb();
+    method public boolean isAutoCancelEnabled();
+    field public static final int FLAG_AE = 2; // 0x2
+    field public static final int FLAG_AF = 1; // 0x1
+    field public static final int FLAG_AWB = 4; // 0x4
+  }
+
+  public static class FocusMeteringAction.Builder {
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint);
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint, @androidx.camera.core.FocusMeteringAction.MeteringMode int);
+    method public androidx.camera.core.FocusMeteringAction build();
+    method public androidx.camera.core.FocusMeteringAction.Builder disableAutoCancel();
+    method public static androidx.camera.core.FocusMeteringAction.Builder from(androidx.camera.core.MeteringPoint);
+    method public static androidx.camera.core.FocusMeteringAction.Builder from(androidx.camera.core.MeteringPoint, @androidx.camera.core.FocusMeteringAction.MeteringMode int);
+    method public androidx.camera.core.FocusMeteringAction.Builder setAutoCancelDuration(@IntRange(from=1) long, java.util.concurrent.TimeUnit);
+  }
+
+  @IntDef(flag=true, value={androidx.camera.core.FocusMeteringAction.FLAG_AF, androidx.camera.core.FocusMeteringAction.FLAG_AE, androidx.camera.core.FocusMeteringAction.FLAG_AWB}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface FocusMeteringAction.MeteringMode {
+  }
+
+  public final class FocusMeteringResult {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.FocusMeteringResult create(boolean);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.FocusMeteringResult emptyInstance();
+    method public boolean isFocusSuccessful();
+  }
+
+  public final class ImageAnalysis extends androidx.camera.core.UseCase {
+    method public void clearAnalyzer();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected java.util.Map<java.lang.String!,android.util.Size!> onSuggestedResolutionUpdated(java.util.Map<java.lang.String!,android.util.Size!>);
+    method public void setAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+    method public void setTargetRotation(int);
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final androidx.camera.core.ImageAnalysis.Defaults! DEFAULT_CONFIG;
+    field public static final int STRATEGY_BLOCK_PRODUCER = 1; // 0x1
+    field public static final int STRATEGY_KEEP_ONLY_LATEST = 0; // 0x0
+  }
+
+  public static interface ImageAnalysis.Analyzer {
+    method public void analyze(androidx.camera.core.ImageProxy);
+  }
+
+  @IntDef({androidx.camera.core.ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST, androidx.camera.core.ImageAnalysis.STRATEGY_BLOCK_PRODUCER}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ImageAnalysis.BackpressureStrategy {
+  }
+
+  public static final class ImageAnalysis.Builder implements androidx.camera.core.impl.UseCaseConfig.Builder<androidx.camera.core.ImageAnalysis,androidx.camera.core.impl.ImageAnalysisConfig,androidx.camera.core.ImageAnalysis.Builder> {
+    ctor public ImageAnalysis.Builder();
+    method public androidx.camera.core.ImageAnalysis build();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.ImageAnalysis.Builder fromConfig(androidx.camera.core.impl.ImageAnalysisConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.MutableConfig getMutableConfig();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.ImageAnalysisConfig getUseCaseConfig();
+    method public androidx.camera.core.ImageAnalysis.Builder setBackgroundExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageAnalysis.Builder setBackpressureStrategy(@androidx.camera.core.ImageAnalysis.BackpressureStrategy int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setCaptureOptionUnpacker(androidx.camera.core.impl.CaptureConfig.OptionUnpacker);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setDefaultCaptureConfig(androidx.camera.core.impl.CaptureConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setDefaultResolution(android.util.Size);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setDefaultSessionConfig(androidx.camera.core.impl.SessionConfig);
+    method public androidx.camera.core.ImageAnalysis.Builder setImageQueueDepth(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setMaxResolution(android.util.Size);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setSessionOptionUnpacker(androidx.camera.core.impl.SessionConfig.OptionUnpacker);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setSupportedResolutions(java.util.List<android.util.Pair<java.lang.Integer!,android.util.Size![]!>!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setSurfaceOccupancyPriority(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetAspectRatio(@androidx.camera.core.AspectRatio.Ratio int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setTargetAspectRatioCustom(android.util.Rational);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setTargetClass(Class<androidx.camera.core.ImageAnalysis!>);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetName(String);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetRotation(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageAnalysis.Builder setUseCaseEventCallback(androidx.camera.core.UseCase.EventCallback);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final class ImageAnalysis.Defaults {
+    ctor public ImageAnalysis.Defaults();
+    method public androidx.camera.core.impl.ImageAnalysisConfig getConfig(androidx.camera.core.CameraInfo?);
+  }
+
+  public class ImageCapture extends androidx.camera.core.UseCase {
+    method @androidx.camera.core.ImageCapture.FlashMode public int getFlashMode();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected java.util.Map<java.lang.String!,android.util.Size!> onSuggestedResolutionUpdated(java.util.Map<java.lang.String!,android.util.Size!>);
+    method public void setFlashMode(@androidx.camera.core.ImageCapture.FlashMode int);
+    method public void setTargetAspectRatioCustom(android.util.Rational);
+    method public void setTargetRotation(int);
+    method public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
+    method public void takePicture(java.io.File, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    method public void takePicture(java.io.File, androidx.camera.core.ImageCapture.Metadata, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    field public static final int CAPTURE_MODE_MAXIMIZE_QUALITY = 0; // 0x0
+    field public static final int CAPTURE_MODE_MINIMIZE_LATENCY = 1; // 0x1
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final androidx.camera.core.ImageCapture.Defaults! DEFAULT_CONFIG;
+    field public static final int ERROR_CAMERA_CLOSED = 3; // 0x3
+    field public static final int ERROR_CAPTURE_FAILED = 2; // 0x2
+    field public static final int ERROR_FILE_IO = 1; // 0x1
+    field public static final int ERROR_INVALID_CAMERA = 4; // 0x4
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int FLASH_MODE_AUTO = 0; // 0x0
+    field public static final int FLASH_MODE_OFF = 2; // 0x2
+    field public static final int FLASH_MODE_ON = 1; // 0x1
+  }
+
+  public static final class ImageCapture.Builder implements androidx.camera.core.impl.UseCaseConfig.Builder<androidx.camera.core.ImageCapture,androidx.camera.core.impl.ImageCaptureConfig,androidx.camera.core.ImageCapture.Builder> {
+    ctor public ImageCapture.Builder();
+    method public androidx.camera.core.ImageCapture build();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.ImageCapture.Builder fromConfig(androidx.camera.core.impl.ImageCaptureConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.MutableConfig getMutableConfig();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.ImageCaptureConfig getUseCaseConfig();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setBufferFormat(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setCameraSelector(androidx.camera.core.CameraSelector);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setCaptureBundle(androidx.camera.core.impl.CaptureBundle);
+    method public androidx.camera.core.ImageCapture.Builder setCaptureMode(@androidx.camera.core.ImageCapture.CaptureMode int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setCaptureOptionUnpacker(androidx.camera.core.impl.CaptureConfig.OptionUnpacker);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setCaptureProcessor(androidx.camera.core.impl.CaptureProcessor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setDefaultCaptureConfig(androidx.camera.core.impl.CaptureConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setDefaultResolution(android.util.Size);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setDefaultSessionConfig(androidx.camera.core.impl.SessionConfig);
+    method public androidx.camera.core.ImageCapture.Builder setFlashMode(@androidx.camera.core.ImageCapture.FlashMode int);
+    method public androidx.camera.core.ImageCapture.Builder setIoExecutor(java.util.concurrent.Executor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setMaxCaptureStages(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setMaxResolution(android.util.Size);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setSessionOptionUnpacker(androidx.camera.core.impl.SessionConfig.OptionUnpacker);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setSupportedResolutions(java.util.List<android.util.Pair<java.lang.Integer!,android.util.Size![]!>!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setSurfaceOccupancyPriority(int);
+    method public androidx.camera.core.ImageCapture.Builder setTargetAspectRatio(@androidx.camera.core.AspectRatio.Ratio int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setTargetAspectRatioCustom(android.util.Rational);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setTargetClass(Class<androidx.camera.core.ImageCapture!>);
+    method public androidx.camera.core.ImageCapture.Builder setTargetName(String);
+    method public androidx.camera.core.ImageCapture.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageCapture.Builder setTargetRotation(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.ImageCapture.Builder setUseCaseEventCallback(androidx.camera.core.UseCase.EventCallback);
+  }
+
+  @IntDef({androidx.camera.core.ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY, androidx.camera.core.ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ImageCapture.CaptureMode {
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final class ImageCapture.Defaults {
+    ctor public ImageCapture.Defaults();
+    method public androidx.camera.core.impl.ImageCaptureConfig getConfig(androidx.camera.core.CameraInfo?);
+  }
+
+  @IntDef({androidx.camera.core.ImageCapture.FLASH_MODE_AUTO, androidx.camera.core.ImageCapture.FLASH_MODE_ON, androidx.camera.core.ImageCapture.FLASH_MODE_OFF}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ImageCapture.FlashMode {
+  }
+
+  @IntDef({androidx.camera.core.ImageCapture.ERROR_UNKNOWN, androidx.camera.core.ImageCapture.ERROR_FILE_IO, androidx.camera.core.ImageCapture.ERROR_CAPTURE_FAILED, androidx.camera.core.ImageCapture.ERROR_CAMERA_CLOSED, androidx.camera.core.ImageCapture.ERROR_INVALID_CAMERA}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ImageCapture.ImageCaptureError {
+  }
+
+  public static final class ImageCapture.Metadata {
+    ctor public ImageCapture.Metadata();
+    method public android.location.Location? getLocation();
+    method public boolean isReversedHorizontal();
+    method public boolean isReversedVertical();
+    method public void setLocation(android.location.Location?);
+    method public void setReversedHorizontal(boolean);
+    method public void setReversedVertical(boolean);
+  }
+
+  public abstract static class ImageCapture.OnImageCapturedCallback {
+    ctor public ImageCapture.OnImageCapturedCallback();
+    method public void onCaptureSuccess(androidx.camera.core.ImageProxy);
+    method public void onError(@androidx.camera.core.ImageCapture.ImageCaptureError int, String, Throwable?);
+  }
+
+  public static interface ImageCapture.OnImageSavedCallback {
+    method public void onError(@androidx.camera.core.ImageCapture.ImageCaptureError int, String, Throwable?);
+    method public void onImageSaved(java.io.File);
+  }
+
+  public interface ImageInfo {
+    method public int getRotationDegrees();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public Object? getTag();
+    method public long getTimestamp();
+  }
+
+  public interface ImageProxy extends java.lang.AutoCloseable {
+    method public void close();
+    method public android.graphics.Rect getCropRect();
+    method public int getFormat();
+    method public int getHeight();
+    method public androidx.camera.core.ImageInfo getImageInfo();
+    method public androidx.camera.core.ImageProxy.PlaneProxy![] getPlanes();
+    method public int getWidth();
+    method public void setCropRect(android.graphics.Rect?);
+  }
+
+  public static interface ImageProxy.PlaneProxy {
+    method public java.nio.ByteBuffer getBuffer();
+    method public int getPixelStride();
+    method public int getRowStride();
+  }
+
+  public class MeteringPoint {
+    method public float getSize();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public android.util.Rational? getSurfaceAspectRatio();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public float getX();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public float getY();
+  }
+
+  public abstract class MeteringPointFactory {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public MeteringPointFactory();
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public MeteringPointFactory(android.util.Rational?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected abstract android.graphics.PointF convertPoint(float, float);
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float);
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float, float);
+    method public static float getDefaultPointSize();
+  }
+
+  public class Preview extends androidx.camera.core.UseCase {
+    method @UiThread public androidx.camera.core.Preview.PreviewSurfaceProvider? getPreviewSurfaceProvider();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected java.util.Map<java.lang.String!,android.util.Size!> onSuggestedResolutionUpdated(java.util.Map<java.lang.String!,android.util.Size!>);
+    method @UiThread public void setPreviewSurfaceProvider(java.util.concurrent.Executor, androidx.camera.core.Preview.PreviewSurfaceProvider?);
+    method @UiThread public void setPreviewSurfaceProvider(androidx.camera.core.Preview.PreviewSurfaceProvider?);
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final androidx.camera.core.Preview.Defaults! DEFAULT_CONFIG;
+  }
+
+  public static final class Preview.Builder implements androidx.camera.core.impl.UseCaseConfig.Builder<androidx.camera.core.Preview,androidx.camera.core.impl.PreviewConfig,androidx.camera.core.Preview.Builder> {
+    ctor public Preview.Builder();
+    method public androidx.camera.core.Preview build();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.camera.core.Preview.Builder fromConfig(androidx.camera.core.impl.PreviewConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.MutableConfig getMutableConfig();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.PreviewConfig getUseCaseConfig();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setBackgroundExecutor(java.util.concurrent.Executor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setCameraSelector(androidx.camera.core.CameraSelector);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setCaptureOptionUnpacker(androidx.camera.core.impl.CaptureConfig.OptionUnpacker);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setCaptureProcessor(androidx.camera.core.impl.CaptureProcessor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setDefaultCaptureConfig(androidx.camera.core.impl.CaptureConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setDefaultResolution(android.util.Size);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setDefaultSessionConfig(androidx.camera.core.impl.SessionConfig);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setImageInfoProcessor(androidx.camera.core.impl.ImageInfoProcessor);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setMaxResolution(android.util.Size);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setSessionOptionUnpacker(androidx.camera.core.impl.SessionConfig.OptionUnpacker);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setSupportedResolutions(java.util.List<android.util.Pair<java.lang.Integer!,android.util.Size![]!>!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setSurfaceOccupancyPriority(int);
+    method public androidx.camera.core.Preview.Builder setTargetAspectRatio(@androidx.camera.core.AspectRatio.Ratio int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setTargetAspectRatioCustom(android.util.Rational);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setTargetClass(Class<androidx.camera.core.Preview!>);
+    method public androidx.camera.core.Preview.Builder setTargetName(String);
+    method public androidx.camera.core.Preview.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.Preview.Builder setTargetRotation(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.Preview.Builder setUseCaseEventCallback(androidx.camera.core.UseCase.EventCallback);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final class Preview.Defaults {
+    ctor public Preview.Defaults();
+    method public androidx.camera.core.impl.PreviewConfig getConfig(androidx.camera.core.CameraInfo?);
+  }
+
+  public static interface Preview.PreviewSurfaceProvider {
+    method public com.google.common.util.concurrent.ListenableFuture<android.view.Surface!> provideSurface(android.util.Size, com.google.common.util.concurrent.ListenableFuture<java.lang.Void!>);
+  }
+
+  public class SurfaceOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public SurfaceOrientedMeteringPointFactory(float, float);
+    ctor public SurfaceOrientedMeteringPointFactory(float, float, androidx.camera.core.UseCase);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected android.graphics.PointF convertPoint(float, float);
+  }
+
+  public class TorchState {
+    field public static final int OFF = 0; // 0x0
+    field public static final int ON = 1; // 0x1
+  }
+
+  @IntDef({androidx.camera.core.TorchState.OFF, androidx.camera.core.TorchState.ON}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface TorchState.State {
+  }
+
+  public abstract class UseCase {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected UseCase(androidx.camera.core.impl.UseCaseConfig<?>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void addStateChangeCallback(androidx.camera.core.UseCase.StateChangeCallback);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected androidx.camera.core.impl.UseCaseConfig<?> applyDefaults(androidx.camera.core.impl.UseCaseConfig<?>, androidx.camera.core.impl.UseCaseConfig.Builder<?,?,?>?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final void attachCameraControl(String!, androidx.camera.core.impl.CameraControlInternal!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected void attachToCamera(String!, androidx.camera.core.impl.SessionConfig!);
+    method @CallSuper @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void clear();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public java.util.Set<java.lang.String!>! getAttachedCameraIds();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public android.util.Size! getAttachedSurfaceResolution(String!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.CameraInternal? getBoundCamera();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected String getBoundCameraId();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected androidx.camera.core.impl.CameraControlInternal! getCameraControl(String!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected androidx.camera.core.impl.UseCaseConfig.Builder<?,?,?>? getDefaultBuilder(androidx.camera.core.CameraInfo?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public int getImageFormat();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public String getName();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.SessionConfig! getSessionConfig(String!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public androidx.camera.core.impl.UseCaseConfig<?>! getUseCaseConfig();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected boolean isCurrentlyBoundCamera(String);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final void notifyActive();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final void notifyInactive();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final void notifyReset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final void notifyState();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final void notifyUpdated();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected void onBind(androidx.camera.core.impl.CameraInternal);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected void onCameraControlReady(String!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void onStateOffline(String);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void onStateOnline(String);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected abstract java.util.Map<java.lang.String!,android.util.Size!> onSuggestedResolutionUpdated(java.util.Map<java.lang.String!,android.util.Size!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void removeStateChangeCallback(androidx.camera.core.UseCase.StateChangeCallback);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected void setImageFormat(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void updateSuggestedResolution(java.util.Map<java.lang.String!,android.util.Size!>!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected final void updateUseCaseConfig(androidx.camera.core.impl.UseCaseConfig<?>);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static interface UseCase.EventCallback {
+    method public void onBind(String);
+    method public void onUnbind();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static interface UseCase.StateChangeCallback {
+    method public void onUseCaseActive(androidx.camera.core.UseCase);
+    method public void onUseCaseInactive(androidx.camera.core.UseCase);
+    method public void onUseCaseReset(androidx.camera.core.UseCase);
+    method public void onUseCaseUpdated(androidx.camera.core.UseCase);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class VideoCapture extends androidx.camera.core.UseCase {
+    ctor public VideoCapture(androidx.camera.core.impl.VideoCaptureConfig!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) protected java.util.Map<java.lang.String!,android.util.Size!> onSuggestedResolutionUpdated(java.util.Map<java.lang.String!,android.util.Size!>);
+    method public void setTargetRotation(int);
+    method public void startRecording(java.io.File, java.util.concurrent.Executor, androidx.camera.core.VideoCapture.OnVideoSavedCallback);
+    method public void startRecording(java.io.File, androidx.camera.core.VideoCapture.Metadata, java.util.concurrent.Executor, androidx.camera.core.VideoCapture.OnVideoSavedCallback);
+    method public void stopRecording();
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final androidx.camera.core.VideoCapture.Defaults! DEFAULT_CONFIG;
+    field public static final int ERROR_ENCODER = 1; // 0x1
+    field public static final int ERROR_MUXER = 2; // 0x2
+    field public static final int ERROR_RECORDING_IN_PROGRESS = 3; // 0x3
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static final class VideoCapture.Defaults {
+    ctor public VideoCapture.Defaults();
+    method public androidx.camera.core.impl.VideoCaptureConfig getConfig(androidx.camera.core.CameraInfo?);
+  }
+
+  public static final class VideoCapture.Metadata {
+    ctor public VideoCapture.Metadata();
+    field public android.location.Location? location;
+  }
+
+  public static interface VideoCapture.OnVideoSavedCallback {
+    method public void onError(@androidx.camera.core.VideoCapture.VideoCaptureError int, String, Throwable?);
+    method public void onVideoSaved(java.io.File);
+  }
+
+  @IntDef({androidx.camera.core.VideoCapture.ERROR_UNKNOWN, androidx.camera.core.VideoCapture.ERROR_ENCODER, androidx.camera.core.VideoCapture.ERROR_MUXER, androidx.camera.core.VideoCapture.ERROR_RECORDING_IN_PROGRESS}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface VideoCapture.VideoCaptureError {
+  }
+
+}
+
+package @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) androidx.camera.core.impl.annotation {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface ExecutedBy {
+    method public abstract String value();
+  }
+
+}
+
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/core/ImageReaderProxysTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageReaderProxysTest.java
similarity index 100%
rename from camera/camera-camera2/src/androidTest/java/androidx/camera/core/ImageReaderProxysTest.java
rename to camera/camera-core/src/androidTest/java/androidx/camera/core/ImageReaderProxysTest.java
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/DisplayOrientedMeteringPointFactory.java b/camera/camera-core/src/main/java/androidx/camera/core/DisplayOrientedMeteringPointFactory.java
index 455ff98..bdd6a79 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/DisplayOrientedMeteringPointFactory.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/DisplayOrientedMeteringPointFactory.java
@@ -16,11 +16,9 @@
 
 package androidx.camera.core;
 
-import android.content.Context;
 import android.graphics.PointF;
 import android.view.Display;
 import android.view.View;
-import android.view.WindowManager;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -42,7 +40,7 @@
  * duty to transform the coordinates properly so that the width and height of this
  * factory represents the full Preview FOV and also the (x,y) passed to create
  * {@link MeteringPoint} needs to be adjusted by apps to the  coordinates left-top (0,0) -
- * right-bottom (width, height). For Example, if the preview is scaled to 2X from the center and
+ * right-bottom (width, height). For example, if the preview is scaled to 2X from the center and
  * is cropped in a {@link View}. Assuming that the dimension of View is (240, 320), then the
  * width/height of this {@link DisplayOrientedMeteringPointFactory} should be (480, 640).  And
  * the (x, y) from the {@link View} should be converted to (x + (480-240)/2, y + (640 - 320)/2)
@@ -65,44 +63,18 @@
 
     /**
      * Creates a {@link DisplayOrientedMeteringPointFactory} for converting View (x, y) into a
-     * {@link MeteringPoint} based on default display's orientation and {@link CameraSelector}.
+     * {@link MeteringPoint} based on the current display's rotation and {@link CameraSelector}.
      *
      * <p>The width/height of this factory forms a coordinate left-top (0, 0) - right-bottom
-     * (width, height) which represents the full camera preview FOV in default display's
+     * (width, height) which represents the full camera preview FOV in the display's
      * orientation. For apps showing full camera preview in a {@link View}, it is as simple as
      * passing View's width/height and passing View (x, y) directly to create a
      * {@link MeteringPoint}. Otherwise the (x, y) passed to
      * {@link MeteringPointFactory#createPoint(float, float)} should be adjusted to this
      * coordinate system first.
      *
-     * @param context        context to get the {@link WindowManager} for default display rotation.
-     * @param cameraSelector current cameraSelector to choose camera.
-     * @param width          the width of the coordinate which are mapped to the full camera preview
-     *                       FOV in default display's orientation.
-     * @param height         the height of the coordinate which are mapped to the full camera
-     *                       preview
-     *                       FOVin default display's orientation.
-     */
-    public DisplayOrientedMeteringPointFactory(@NonNull Context context,
-            @NonNull CameraSelector cameraSelector, float width, float height) {
-        this(((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(),
-                cameraSelector, width, height);
-    }
-
-    /**
-     * Creates a {@link DisplayOrientedMeteringPointFactory} for converting View (x, y) into a
-     * {@link MeteringPoint} based on custom display's rotation and {@link CameraSelector}. This is
-     * used in multi-display situation.
-     *
-     * <p>The width/height of this factory forms a coordinate left-top (0, 0) - right-bottom
-     * (width, height) which represents the full camera preview FOV in default display's
-     * orientation. For apps showing full camera preview in a {@link View}, it is as simple as
-     * passing View's width/height and passing View (x, y) directly to create a
-     * {@link MeteringPoint}. Otherwise the (x, y) passed to
-     * {@link MeteringPointFactory#createPoint(float, float)} should be adjusted to this
-     * coordinate system first.
-     *
-     * @param display        {@link Display} to get the orientation from.
+     * @param display        {@link Display} to get the orientation from. This should be the
+     *                       current display where camera preview is showing.
      * @param cameraSelector current cameraSelector to choose camera.
      * @param width          the width of the coordinate which are mapped to the full camera preview
      *                       FOV in given display's orientation.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/FocusMeteringAction.java b/camera/camera-core/src/main/java/androidx/camera/core/FocusMeteringAction.java
index 27b7778..c09b700 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/FocusMeteringAction.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/FocusMeteringAction.java
@@ -34,15 +34,19 @@
 /**
  * A configuration used to trigger a focus and/or metering action.
  *
- * <p>To construct a {@link FocusMeteringAction}, apps have to create a {@link Builder} by
- * {@link Builder#from(MeteringPoint)} or {@link Builder#from(MeteringPoint, int)}.
- * {@link MeteringPoint} is a point used to specify the focus/metering areas. Apps can use various
- * {@link MeteringPointFactory} to create the points. When the {@link FocusMeteringAction} is built,
- * pass it to {@link CameraControl#startFocusAndMetering(FocusMeteringAction)} to initiate the focus
+ * <p>A {@link FocusMeteringAction} must be created by the {@link Builder}. To construct a
+ * {@link Builder}, a {@link MeteringPoint} is required to specify the focus/metering area. Apps
+ * can use various {@link MeteringPointFactory} to create the points. After the
+ * {@link FocusMeteringAction} is built, apps can pass it to
+ * {@link CameraControl#startFocusAndMetering(FocusMeteringAction)} to initiate the focus
  * and metering action.
  *
- * <p>The default meteringMode is {@link #FLAG_AF} | {@link #FLAG_AE} | {@link #FLAG_AWB} which
- * means the point is used for all AF/AE/AWB regions. Apps can set the proper meteringMode to
+ * <p>When specifying a {@link MeteringPoint}, a metering mode can also be specified. Metering
+ * mode is a combination of flags consisting of {@link #FLAG_AF}, {@link #FLAG_AE}, and
+ * {@link #FLAG_AWB}. This combination indicates whether the {@link MeteringPoint} is
+ * used to set AF(Auto Focus) region, AE(Auto Exposure) region or AWB(Auto White Balance) region.
+ * The default meteringMode is {@link #FLAG_AF} | {@link #FLAG_AE} | {@link #FLAG_AWB} which
+ * means the point is used for all AF/AE/AWB regions. Apps can set the proper metering mode to
  * optionally exclude some 3A regions. Multiple regions for specific 3A type are also supported
  * via {@link Builder#addPoint(MeteringPoint)} or {@link Builder#addPoint(MeteringPoint, int)}.
  * App can also this API to enable different region for AF and AE respectively.
@@ -65,12 +69,24 @@
  */
 public final class FocusMeteringAction {
 
+    /**
+     * A flag used in metering mode indicating the AF (Auto Focus) region is enabled. An autofocus
+     * scan is also triggered when FLAG_AF is assigned.
+     */
     public static final int FLAG_AF = 1;
+
+    /**
+     * A flag used in metering mode indicating the AE (Auto Exposure) region is enabled.
+     */
     public static final int FLAG_AE = 1 << 1;
+
+    /**
+     * A flag used in metering mode indicating the AWB (Auto White Balance) region is enabled.
+     */
     public static final int FLAG_AWB = 1 << 2;
 
     @MeteringMode
-    static final int DEFAULT_METERINGMODE = FLAG_AF | FLAG_AE | FLAG_AWB;
+    static final int DEFAULT_METERING_MODE = FLAG_AF | FLAG_AE | FLAG_AWB;
     static final long DEFAULT_AUTOCANCEL_DURATION = 5000;
     private final List<MeteringPoint> mMeteringPointsAf;
     private final List<MeteringPoint> mMeteringPointsAe;
@@ -136,9 +152,7 @@
     }
 
     /**
-     * The builder used to create the {@link FocusMeteringAction}. App must use
-     * {@link Builder#from(MeteringPoint)}
-     * or {@link Builder#from(MeteringPoint, int)} to create the {@link Builder}.
+     * The builder used to create the {@link FocusMeteringAction}.
      */
     public static class Builder {
         @SuppressWarnings("WeakerAccess") /* synthetic accessor */
@@ -150,38 +164,32 @@
         @SuppressWarnings("WeakerAccess") /* synthetic accessor */
                 long mAutoCancelDurationInMillis = DEFAULT_AUTOCANCEL_DURATION;
 
-        private Builder(@NonNull MeteringPoint point) {
-            this(point, DEFAULT_METERINGMODE);
-        }
-
-        private Builder(@NonNull MeteringPoint point, @MeteringMode int mode) {
-            addPoint(point, mode);
-        }
-
         /**
-         * Creates the Builder from a {@link MeteringPoint} with default
-         * mode {@link #FLAG_AF} | {@link #FLAG_AE} | {@link #FLAG_AWB}.
+         * Creates the Builder from a {@link MeteringPoint} with default mode {@link #FLAG_AF} |
+         * {@link #FLAG_AE} | {@link #FLAG_AWB}.
          */
-        @NonNull
-        public static Builder from(@NonNull MeteringPoint meteringPoint) {
-            return new Builder(meteringPoint);
+        public Builder(@NonNull MeteringPoint point) {
+            this(point, DEFAULT_METERING_MODE);
         }
 
         /**
          * Creates the Builder from a {@link MeteringPoint} and MeteringMode.
+         *
+         * <p>Metering mode is a combination of flags consisting of {@link #FLAG_AF},
+         * {@link #FLAG_AE}, and {@link #FLAG_AWB}. This combination indicates whether the
+         * {@link MeteringPoint} is used to set AF(Auto Focus) region, AE(Auto
+         * Exposure) region or AWB(Auto White Balance) region.
          */
-        @NonNull
-        public static Builder from(@NonNull MeteringPoint meteringPoint, @MeteringMode int mode) {
-            return new Builder(meteringPoint, mode);
+        public Builder(@NonNull MeteringPoint point, @MeteringMode int meteringMode) {
+            addPoint(point, meteringMode);
         }
 
         /**
-         * Adds another {@link MeteringPoint} with default mode {@link #FLAG_AF} |
+         * Adds another {@link MeteringPoint} with default metering mode {@link #FLAG_AF} |
          * {@link #FLAG_AE} | {@link #FLAG_AWB}.
          *
-         * <p>The points added here will be appended in order after the point set in
-         * {@link FocusMeteringAction.Builder#from(MeteringPoint)} or
-         * {@link FocusMeteringAction.Builder#from(MeteringPoint, int)}.
+         * <p>The points added here will be appended in order after the point set in builder
+         * constructor.
          *
          * <p>If more points are added than what current device supports for AF/AE/AWB, only the
          * first point and then in order up to the number of points supported on the device
@@ -195,15 +203,19 @@
          */
         @NonNull
         public Builder addPoint(@NonNull MeteringPoint point) {
-            return addPoint(point, DEFAULT_METERINGMODE);
+            return addPoint(point, DEFAULT_METERING_MODE);
         }
 
         /**
          * Adds another {@link MeteringPoint} with specified meteringMode.
          *
-         * <p>The points added here will be appended in order after the point set in
-         * {@link FocusMeteringAction.Builder#from(MeteringPoint)} or
-         * {@link FocusMeteringAction.Builder#from(MeteringPoint, int)}.
+         * <p>Metering mode is a combination of flags consisting of {@link #FLAG_AF},
+         * {@link #FLAG_AE}, and {@link #FLAG_AWB}. This combination indicates whether the
+         * {@link MeteringPoint} is used to set AF(Auto Focus) region, AE(Auto Exposure) region
+         * or AWB(Auto White Balance) region.
+         *
+         * <p>The points added here will be appended in order after the point set in builder
+         * constructor.
          *
          * <p>If more points are added than what current device supports for AF/AE/AWB, only the
          * first point and then in order up to the number of points supported on the device
@@ -216,18 +228,19 @@
          * @see CameraControl#startFocusAndMetering(FocusMeteringAction)
          */
         @NonNull
-        public Builder addPoint(@NonNull MeteringPoint point, @MeteringMode int mode) {
+        public Builder addPoint(@NonNull MeteringPoint point, @MeteringMode int meteringMode) {
+            Preconditions.checkArgument(point != null, "Point cannot be null.");
             Preconditions.checkArgument(
-                    (mode >= FLAG_AF) && (mode <= (FLAG_AF | FLAG_AE | FLAG_AWB)),
-                    "Invalid metering mode " + mode);
+                    (meteringMode >= FLAG_AF) && (meteringMode <= (FLAG_AF | FLAG_AE | FLAG_AWB)),
+                    "Invalid metering mode " + meteringMode);
 
-            if ((mode & FLAG_AF) != 0) {
+            if ((meteringMode & FLAG_AF) != 0) {
                 mMeteringPointsAf.add(point);
             }
-            if ((mode & FLAG_AE) != 0) {
+            if ((meteringMode & FLAG_AE) != 0) {
                 mMeteringPointsAe.add(point);
             }
-            if ((mode & FLAG_AWB) != 0) {
+            if ((meteringMode & FLAG_AWB) != 0) {
                 mMeteringPointsAwb.add(point);
             }
             return this;
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageReaderProxys.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageReaderProxys.java
index 1421c48..f4cdc15 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageReaderProxys.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageReaderProxys.java
@@ -19,17 +19,12 @@
 import android.media.ImageReader;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
 import androidx.camera.core.impl.ImageReaderProxy;
 
 /**
  * Different implementations of {@link ImageReaderProxy}.
- *
- * @hide
  */
-@RestrictTo(Scope.LIBRARY_GROUP)
-public final class ImageReaderProxys {
+final class ImageReaderProxys {
 
     private ImageReaderProxys() {
     }
@@ -44,7 +39,7 @@
      * @return new {@link ImageReaderProxy} instance
      */
     @NonNull
-    public static ImageReaderProxy createIsolatedReader(
+    static ImageReaderProxy createIsolatedReader(
             int width, int height, int format, int maxImages) {
         ImageReader imageReader = ImageReader.newInstance(width, height, format, maxImages);
         return new AndroidImageReaderProxy(imageReader);
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 c7473ff..f5e4eb1 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
@@ -335,10 +335,9 @@
                     if (!mCaptureIdList.contains(tag)) {
                         Log.w(TAG, "ImageProxyBundle does not contain this id: " + tag);
                         image.close();
-                        return;
+                    } else {
+                        mSettableImageProxyBundle.addImageProxy(image);
                     }
-
-                    mSettableImageProxyBundle.addImageProxy(image);
                 }
             }
         }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/SettableImageProxy.java b/camera/camera-core/src/main/java/androidx/camera/core/SettableImageProxy.java
index c17e8f1..542c67c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/SettableImageProxy.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/SettableImageProxy.java
@@ -35,6 +35,7 @@
         mImageInfo = imageInfo;
     }
 
+    @SuppressWarnings("UnsynchronizedOverridesSynchronized")
     @Override
     @NonNull
     public ImageInfo getImageInfo() {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/executor/HighPriorityExecutor.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/executor/HighPriorityExecutor.java
index 99702cc..ea835b9 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/executor/HighPriorityExecutor.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/executor/HighPriorityExecutor.java
@@ -36,6 +36,7 @@
                         private static final String THREAD_NAME =
                                 CameraXThreads.TAG + "camerax_high_priority";
 
+                        @SuppressWarnings("ThreadPriorityCheck")
                         @Override
                         public Thread newThread(Runnable r) {
                             Thread t = new Thread(r);
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/FocusMeteringActionTest.java b/camera/camera-core/src/test/java/androidx/camera/core/FocusMeteringActionTest.java
index 5ae62c87..c4adaed 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/FocusMeteringActionTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/FocusMeteringActionTest.java
@@ -40,7 +40,7 @@
 
     @Test
     public void defaultBuilder_valueIsDefault() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1).build();
 
         assertThat(action.getMeteringPointsAf()).containsExactly(mPoint1);
         assertThat(action.getMeteringPointsAe()).containsExactly(mPoint1);
@@ -52,9 +52,10 @@
 
     @Test
     public void fromPointWithAFAEAWB() {
-        FocusMeteringAction action = FocusMeteringAction.Builder
-                .from(mPoint1, FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE
-                        | FocusMeteringAction.FLAG_AWB).build();
+        FocusMeteringAction action =
+                new FocusMeteringAction.Builder(mPoint1,
+                        FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE
+                                | FocusMeteringAction.FLAG_AWB).build();
         assertThat(action.getMeteringPointsAf()).containsExactly(mPoint1);
         assertThat(action.getMeteringPointsAe()).containsExactly(mPoint1);
         assertThat(action.getMeteringPointsAwb()).containsExactly(mPoint1);
@@ -62,8 +63,8 @@
 
     @Test
     public void fromPointWithAFAE() {
-        FocusMeteringAction action = FocusMeteringAction.Builder
-                .from(mPoint1, FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
+                FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE).build();
         assertThat(action.getMeteringPointsAf()).containsExactly(mPoint1);
         assertThat(action.getMeteringPointsAe()).containsExactly(mPoint1);
         assertThat(action.getMeteringPointsAwb()).isEmpty();
@@ -71,8 +72,8 @@
 
     @Test
     public void fromPointWithAFAWB() {
-        FocusMeteringAction action = FocusMeteringAction.Builder
-                .from(mPoint1, FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AWB).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
+                FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AWB).build();
         assertThat(action.getMeteringPointsAf()).containsExactly(mPoint1);
         assertThat(action.getMeteringPointsAe()).isEmpty();
         assertThat(action.getMeteringPointsAwb()).containsExactly(mPoint1);
@@ -80,8 +81,8 @@
 
     @Test
     public void fromPointWithAEAWB() {
-        FocusMeteringAction action = FocusMeteringAction.Builder
-                .from(mPoint1, FocusMeteringAction.FLAG_AE | FocusMeteringAction.FLAG_AWB).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
+                FocusMeteringAction.FLAG_AE | FocusMeteringAction.FLAG_AWB).build();
         assertThat(action.getMeteringPointsAf()).isEmpty();
         assertThat(action.getMeteringPointsAe()).containsExactly(mPoint1);
         assertThat(action.getMeteringPointsAwb()).containsExactly(mPoint1);
@@ -89,8 +90,8 @@
 
     @Test
     public void fromPointWithAF() {
-        FocusMeteringAction action = FocusMeteringAction.Builder
-                .from(mPoint1, FocusMeteringAction.FLAG_AF).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
+                FocusMeteringAction.FLAG_AF).build();
         assertThat(action.getMeteringPointsAf()).containsExactly(mPoint1);
         assertThat(action.getMeteringPointsAe()).isEmpty();
         assertThat(action.getMeteringPointsAwb()).isEmpty();
@@ -98,8 +99,8 @@
 
     @Test
     public void fromPointWithAE() {
-        FocusMeteringAction action = FocusMeteringAction.Builder
-                .from(mPoint1, FocusMeteringAction.FLAG_AE).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
+                FocusMeteringAction.FLAG_AE).build();
         assertThat(action.getMeteringPointsAf()).isEmpty();
         assertThat(action.getMeteringPointsAe()).containsExactly(mPoint1);
         assertThat(action.getMeteringPointsAwb()).isEmpty();
@@ -107,8 +108,8 @@
 
     @Test
     public void fromPointWithAWB() {
-        FocusMeteringAction action = FocusMeteringAction.Builder
-                .from(mPoint1, FocusMeteringAction.FLAG_AWB).build();
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
+                FocusMeteringAction.FLAG_AWB).build();
         assertThat(action.getMeteringPointsAf()).isEmpty();
         assertThat(action.getMeteringPointsAe()).isEmpty();
         assertThat(action.getMeteringPointsAwb()).containsExactly(mPoint1);
@@ -116,7 +117,7 @@
 
     @Test
     public void multiplePointsWithDefaultMeteringMode() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .addPoint(mPoint2)
                 .addPoint(mPoint3)
                 .build();
@@ -127,10 +128,9 @@
 
     @Test
     public void multiplePointsWithSameAF_AE_AWB() {
-        FocusMeteringAction action = FocusMeteringAction.Builder
-                .from(mPoint1,
-                        FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE
-                                | FocusMeteringAction.FLAG_AWB)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
+                FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE
+                        | FocusMeteringAction.FLAG_AWB)
                 .addPoint(mPoint2,
                         FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE
                                 | FocusMeteringAction.FLAG_AWB)
@@ -145,8 +145,8 @@
 
     @Test
     public void multiplePointsWithSameAF_AE() {
-        FocusMeteringAction action = FocusMeteringAction.Builder
-                .from(mPoint1, FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
+                FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE)
                 .addPoint(mPoint2, FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE)
                 .addPoint(mPoint3, FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE)
                 .build();
@@ -157,8 +157,8 @@
 
     @Test
     public void multiplePointsWithSameAE_AWB() {
-        FocusMeteringAction action = FocusMeteringAction.Builder
-                .from(mPoint1, FocusMeteringAction.FLAG_AE | FocusMeteringAction.FLAG_AWB)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
+                FocusMeteringAction.FLAG_AE | FocusMeteringAction.FLAG_AWB)
                 .addPoint(mPoint2, FocusMeteringAction.FLAG_AE | FocusMeteringAction.FLAG_AWB)
                 .addPoint(mPoint3, FocusMeteringAction.FLAG_AE | FocusMeteringAction.FLAG_AWB)
                 .build();
@@ -169,8 +169,8 @@
 
     @Test
     public void multiplePointsWithSameAF_AWB() {
-        FocusMeteringAction action = FocusMeteringAction.Builder
-                .from(mPoint1, FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AWB)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
+                FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AWB)
                 .addPoint(mPoint2, FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AWB)
                 .addPoint(mPoint3, FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AWB)
                 .build();
@@ -181,7 +181,7 @@
 
     @Test
     public void multiplePointsWithSameAWBOnly() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FocusMeteringAction.FLAG_AWB)
                 .addPoint(mPoint2, FocusMeteringAction.FLAG_AWB)
                 .addPoint(mPoint3, FocusMeteringAction.FLAG_AWB)
@@ -193,7 +193,7 @@
 
     @Test
     public void multiplePointsWithSameAEOnly() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FocusMeteringAction.FLAG_AE)
                 .addPoint(mPoint2, FocusMeteringAction.FLAG_AE)
                 .addPoint(mPoint3, FocusMeteringAction.FLAG_AE)
@@ -205,7 +205,7 @@
 
     @Test
     public void multiplePointsWithSameAFOnly() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FocusMeteringAction.FLAG_AF)
                 .addPoint(mPoint2, FocusMeteringAction.FLAG_AF)
                 .addPoint(mPoint3, FocusMeteringAction.FLAG_AF)
@@ -217,7 +217,7 @@
 
     @Test
     public void multiplePointsWithAFOnly_AEOnly_AWBOnly() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FocusMeteringAction.FLAG_AF)
                 .addPoint(mPoint2, FocusMeteringAction.FLAG_AE)
                 .addPoint(mPoint3, FocusMeteringAction.FLAG_AWB)
@@ -229,7 +229,7 @@
 
     @Test
     public void multiplePointsWithAFAE_AEAWB_AFAWB() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE)
                 .addPoint(mPoint2, FocusMeteringAction.FLAG_AE | FocusMeteringAction.FLAG_AWB)
                 .addPoint(mPoint3, FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AWB)
@@ -241,7 +241,7 @@
 
     @Test
     public void multiplePointsWithAFAEAWB_AEAWB_AFOnly() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE
                         | FocusMeteringAction.FLAG_AWB)
                 .addPoint(mPoint2, FocusMeteringAction.FLAG_AE | FocusMeteringAction.FLAG_AWB)
@@ -254,7 +254,7 @@
 
     @Test
     public void multiplePointsWithAEOnly_AFAWAEB_AEOnly() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1,
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1,
                 FocusMeteringAction.FLAG_AE)
                 .addPoint(mPoint2,
                         FocusMeteringAction.FLAG_AF | FocusMeteringAction.FLAG_AE
@@ -268,7 +268,7 @@
 
     @Test
     public void setAutoCancelDurationBySeconds() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .setAutoCancelDuration(3, TimeUnit.SECONDS)
                 .build();
         assertThat(action.getAutoCancelDurationInMillis()).isEqualTo(3000);
@@ -276,7 +276,7 @@
 
     @Test
     public void setAutoCancelDurationByMinutes() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .setAutoCancelDuration(2, TimeUnit.MINUTES)
                 .build();
         assertThat(action.getAutoCancelDurationInMillis()).isEqualTo(120000);
@@ -284,7 +284,7 @@
 
     @Test
     public void setAutoCancelDurationByMilliseconds() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .setAutoCancelDuration(1500, TimeUnit.MILLISECONDS)
                 .build();
         assertThat(action.getAutoCancelDurationInMillis()).isEqualTo(1500);
@@ -292,7 +292,7 @@
 
     @Test
     public void setAutoCancelDurationLargerThan0_shouldEnableAutoCancel() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .setAutoCancelDuration(1, TimeUnit.MILLISECONDS)
                 .build();
 
@@ -301,15 +301,26 @@
 
     @Test(expected = IllegalArgumentException.class)
     public void setAutoCancelDuration0_shouldThrowException() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .setAutoCancelDuration(0, TimeUnit.MILLISECONDS)
                 .build();
     }
 
     @Test(expected = IllegalArgumentException.class)
     public void setAutoCancelDurationSmallerThan0_shouldThrowException() {
-        FocusMeteringAction action = FocusMeteringAction.Builder.from(mPoint1)
+        FocusMeteringAction action = new FocusMeteringAction.Builder(mPoint1)
                 .setAutoCancelDuration(-1, TimeUnit.MILLISECONDS)
                 .build();
     }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void builderWithNullPoint() {
+        new FocusMeteringAction.Builder(null).build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void builderWithNullPoint2() {
+        new FocusMeteringAction.Builder(null, FocusMeteringAction.FLAG_AF).build();
+    }
+
 }
diff --git a/camera/camera-lifecycle/api/1.0.0-alpha02.txt b/camera/camera-lifecycle/api/1.0.0-alpha02.txt
new file mode 100644
index 0000000..a6aba27
--- /dev/null
+++ b/camera/camera-lifecycle/api/1.0.0-alpha02.txt
@@ -0,0 +1,21 @@
+// Signature format: 3.0
+package androidx.camera.lifecycle {
+
+  public interface LifecycleCameraProvider {
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method public void unbind(androidx.camera.core.UseCase!...);
+    method public void unbindAll();
+  }
+
+  public final class ProcessCameraProvider implements androidx.camera.lifecycle.LifecycleCameraProvider {
+    method public androidx.camera.core.Camera bindToLifecycle(androidx.lifecycle.LifecycleOwner, androidx.camera.core.CameraSelector, androidx.camera.core.UseCase!...);
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.lifecycle.ProcessCameraProvider!> getInstance(android.content.Context);
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method public void unbind(androidx.camera.core.UseCase!...);
+    method public void unbindAll();
+  }
+
+}
+
diff --git a/camera/camera-lifecycle/api/current.txt b/camera/camera-lifecycle/api/current.txt
new file mode 100644
index 0000000..a6aba27
--- /dev/null
+++ b/camera/camera-lifecycle/api/current.txt
@@ -0,0 +1,21 @@
+// Signature format: 3.0
+package androidx.camera.lifecycle {
+
+  public interface LifecycleCameraProvider {
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method public void unbind(androidx.camera.core.UseCase!...);
+    method public void unbindAll();
+  }
+
+  public final class ProcessCameraProvider implements androidx.camera.lifecycle.LifecycleCameraProvider {
+    method public androidx.camera.core.Camera bindToLifecycle(androidx.lifecycle.LifecycleOwner, androidx.camera.core.CameraSelector, androidx.camera.core.UseCase!...);
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.lifecycle.ProcessCameraProvider!> getInstance(android.content.Context);
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method public void unbind(androidx.camera.core.UseCase!...);
+    method public void unbindAll();
+  }
+
+}
+
diff --git a/camera/camera-lifecycle/api/public_plus_experimental_1.0.0-alpha02.txt b/camera/camera-lifecycle/api/public_plus_experimental_1.0.0-alpha02.txt
new file mode 100644
index 0000000..a6aba27
--- /dev/null
+++ b/camera/camera-lifecycle/api/public_plus_experimental_1.0.0-alpha02.txt
@@ -0,0 +1,21 @@
+// Signature format: 3.0
+package androidx.camera.lifecycle {
+
+  public interface LifecycleCameraProvider {
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method public void unbind(androidx.camera.core.UseCase!...);
+    method public void unbindAll();
+  }
+
+  public final class ProcessCameraProvider implements androidx.camera.lifecycle.LifecycleCameraProvider {
+    method public androidx.camera.core.Camera bindToLifecycle(androidx.lifecycle.LifecycleOwner, androidx.camera.core.CameraSelector, androidx.camera.core.UseCase!...);
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.lifecycle.ProcessCameraProvider!> getInstance(android.content.Context);
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method public void unbind(androidx.camera.core.UseCase!...);
+    method public void unbindAll();
+  }
+
+}
+
diff --git a/camera/camera-lifecycle/api/public_plus_experimental_current.txt b/camera/camera-lifecycle/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..a6aba27
--- /dev/null
+++ b/camera/camera-lifecycle/api/public_plus_experimental_current.txt
@@ -0,0 +1,21 @@
+// Signature format: 3.0
+package androidx.camera.lifecycle {
+
+  public interface LifecycleCameraProvider {
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method public void unbind(androidx.camera.core.UseCase!...);
+    method public void unbindAll();
+  }
+
+  public final class ProcessCameraProvider implements androidx.camera.lifecycle.LifecycleCameraProvider {
+    method public androidx.camera.core.Camera bindToLifecycle(androidx.lifecycle.LifecycleOwner, androidx.camera.core.CameraSelector, androidx.camera.core.UseCase!...);
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.lifecycle.ProcessCameraProvider!> getInstance(android.content.Context);
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method public void unbind(androidx.camera.core.UseCase!...);
+    method public void unbindAll();
+  }
+
+}
+
diff --git a/savedstate/api/res-1.0.0-alpha02.txt b/camera/camera-lifecycle/api/res-1.0.0-alpha02.txt
similarity index 100%
copy from savedstate/api/res-1.0.0-alpha02.txt
copy to camera/camera-lifecycle/api/res-1.0.0-alpha02.txt
diff --git a/camera/camera-lifecycle/api/restricted_1.0.0-alpha02.txt b/camera/camera-lifecycle/api/restricted_1.0.0-alpha02.txt
new file mode 100644
index 0000000..a6aba27
--- /dev/null
+++ b/camera/camera-lifecycle/api/restricted_1.0.0-alpha02.txt
@@ -0,0 +1,21 @@
+// Signature format: 3.0
+package androidx.camera.lifecycle {
+
+  public interface LifecycleCameraProvider {
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method public void unbind(androidx.camera.core.UseCase!...);
+    method public void unbindAll();
+  }
+
+  public final class ProcessCameraProvider implements androidx.camera.lifecycle.LifecycleCameraProvider {
+    method public androidx.camera.core.Camera bindToLifecycle(androidx.lifecycle.LifecycleOwner, androidx.camera.core.CameraSelector, androidx.camera.core.UseCase!...);
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.lifecycle.ProcessCameraProvider!> getInstance(android.content.Context);
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method public void unbind(androidx.camera.core.UseCase!...);
+    method public void unbindAll();
+  }
+
+}
+
diff --git a/camera/camera-lifecycle/api/restricted_current.txt b/camera/camera-lifecycle/api/restricted_current.txt
new file mode 100644
index 0000000..a6aba27
--- /dev/null
+++ b/camera/camera-lifecycle/api/restricted_current.txt
@@ -0,0 +1,21 @@
+// Signature format: 3.0
+package androidx.camera.lifecycle {
+
+  public interface LifecycleCameraProvider {
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method public void unbind(androidx.camera.core.UseCase!...);
+    method public void unbindAll();
+  }
+
+  public final class ProcessCameraProvider implements androidx.camera.lifecycle.LifecycleCameraProvider {
+    method public androidx.camera.core.Camera bindToLifecycle(androidx.lifecycle.LifecycleOwner, androidx.camera.core.CameraSelector, androidx.camera.core.UseCase!...);
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.lifecycle.ProcessCameraProvider!> getInstance(android.content.Context);
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method public void unbind(androidx.camera.core.UseCase!...);
+    method public void unbindAll();
+  }
+
+}
+
diff --git a/camera/camera-view/src/androidTest/java/androidx/camera/view/TextureViewMeteringPointFactoryTest.java b/camera/camera-view/src/androidTest/java/androidx/camera/view/TextureViewMeteringPointFactoryTest.java
index 2f9e7c5..3ef79dc 100644
--- a/camera/camera-view/src/androidTest/java/androidx/camera/view/TextureViewMeteringPointFactoryTest.java
+++ b/camera/camera-view/src/androidTest/java/androidx/camera/view/TextureViewMeteringPointFactoryTest.java
@@ -28,9 +28,11 @@
 import android.content.Context;
 import android.graphics.SurfaceTexture;
 import android.util.Size;
+import android.view.Display;
 import android.view.TextureView;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowManager;
 
 import androidx.annotation.NonNull;
 import androidx.camera.camera2.Camera2Config;
@@ -94,7 +96,7 @@
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     private FakeLifecycleOwner mLifecycle;
     private CountDownLatch mLatchForFrameReady;
-    private Context mContext;
+    private Display mDisplay;
     private TextureView mTextureView;
     private int mWidth;
     private int mHeight;
@@ -104,12 +106,15 @@
         assumeTrue(CameraUtil.deviceHasCamera());
         CoreAppTestUtil.assumeCompatibleDevice();
 
-        mContext = ApplicationProvider.getApplicationContext();
+        Context context = ApplicationProvider.getApplicationContext();
+        WindowManager windowManager =
+                ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE));
+        mDisplay = windowManager.getDefaultDisplay();
         CameraXConfig config = Camera2Config.defaultConfig();
-        CameraX.initialize(mContext, config);
+        CameraX.initialize(context, config);
         mLifecycle = new FakeLifecycleOwner();
         mLatchForFrameReady = new CountDownLatch(1);
-        mTextureView = new TextureView(mContext);
+        mTextureView = new TextureView(context);
         setContentView(mTextureView);
     }
 
@@ -131,7 +136,7 @@
 
         // Creates the DisplayOrientedMeteringPointFactory with same width / height as TextureView
         DisplayOrientedMeteringPointFactory displayFactory =
-                new DisplayOrientedMeteringPointFactory(mContext, BACK_CAM,
+                new DisplayOrientedMeteringPointFactory(mDisplay, BACK_CAM,
                         mTextureView.getWidth(), mTextureView.getHeight());
 
         // Uses DisplayOrientedMeteringPointFactory to verify if coordinates are correct.
@@ -150,7 +155,7 @@
 
         // Creates the DisplayOrientedMeteringPointFactory with same width / height as TextureView
         DisplayOrientedMeteringPointFactory displayFactory =
-                new DisplayOrientedMeteringPointFactory(mContext, FRONT_CAM,
+                new DisplayOrientedMeteringPointFactory(mDisplay, FRONT_CAM,
                         mWidth, mHeight);
 
         // Uses DisplayOrientedMeteringPointFactory to verify if coordinates are correct.
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/CameraView.java b/camera/camera-view/src/main/java/androidx/camera/view/CameraView.java
index 0dfbf2c..d057c7c 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/CameraView.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/CameraView.java
@@ -799,7 +799,7 @@
         if (camera != null) {
             ListenableFuture<FocusMeteringResult> future =
                     camera.getCameraControl().startFocusAndMetering(
-                            FocusMeteringAction.Builder.from(afPoint,
+                            new FocusMeteringAction.Builder(afPoint,
                                     FocusMeteringAction.FLAG_AF).addPoint(aePoint,
                                     FocusMeteringAction.FLAG_AE).build());
             Futures.addCallback(future, new FutureCallback<FocusMeteringResult>() {
diff --git a/car-app/OWNERS b/car-app/OWNERS
deleted file mode 100644
index 0ee2ded..0000000
--- a/car-app/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-jorgeper@google.com
-shiufai@google.com
-
diff --git a/car-app/app/api/1.0.0-alpha01.txt b/car-app/app/api/1.0.0-alpha01.txt
deleted file mode 100644
index da4f6cc..0000000
--- a/car-app/app/api/1.0.0-alpha01.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 3.0
diff --git a/car-app/app/api/current.txt b/car-app/app/api/current.txt
deleted file mode 100644
index da4f6cc..0000000
--- a/car-app/app/api/current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 3.0
diff --git a/car-app/app/api/public_plus_experimental_1.0.0-alpha01.txt b/car-app/app/api/public_plus_experimental_1.0.0-alpha01.txt
deleted file mode 100644
index da4f6cc..0000000
--- a/car-app/app/api/public_plus_experimental_1.0.0-alpha01.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 3.0
diff --git a/car-app/app/api/public_plus_experimental_current.txt b/car-app/app/api/public_plus_experimental_current.txt
deleted file mode 100644
index da4f6cc..0000000
--- a/car-app/app/api/public_plus_experimental_current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 3.0
diff --git a/car-app/app/api/restricted_1.0.0-alpha01.txt b/car-app/app/api/restricted_1.0.0-alpha01.txt
deleted file mode 100644
index da4f6cc..0000000
--- a/car-app/app/api/restricted_1.0.0-alpha01.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 3.0
diff --git a/car-app/app/api/restricted_current.txt b/car-app/app/api/restricted_current.txt
deleted file mode 100644
index da4f6cc..0000000
--- a/car-app/app/api/restricted_current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 3.0
diff --git a/car-app/app/build.gradle b/car-app/app/build.gradle
deleted file mode 100644
index a02915c..0000000
--- a/car-app/app/build.gradle
+++ /dev/null
@@ -1,22 +0,0 @@
-import static androidx.build.dependencies.DependenciesKt.*
-import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
-import androidx.build.Publish
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.library")
-}
-
-dependencies {
-    /* Add dependencies here */
-}
-
-androidx {
-    name = "AndroidX Car App"
-    publish = Publish.SNAPSHOT_ONLY
-    mavenVersion = LibraryVersions.CAR_APP
-    mavenGroup = LibraryGroups.CAR_APP
-    inceptionYear = "2019"
-    description = "Library for building navigation apps for Android Auto"
-}
diff --git a/car-app/app/src/androidTest/AndroidManifest.xml b/car-app/app/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index c3e6f73..0000000
--- a/car-app/app/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="androidx.car.app.test">
-</manifest>
\ No newline at end of file
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/AbstractCodegenSignatureTest.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/AbstractCodegenSignatureTest.kt
new file mode 100644
index 0000000..b4ee9fc
--- /dev/null
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/AbstractCodegenSignatureTest.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.plugins.kotlin
+
+import org.jetbrains.kotlin.backend.common.output.OutputFile
+
+abstract class AbstractCodegenSignatureTest : AbstractCodegenTest() {
+
+    private var isSetup = false
+    override fun setUp() {
+        isSetup = true
+        super.setUp()
+    }
+
+    private fun <T> ensureSetup(block: () -> T): T {
+        if (!isSetup) setUp()
+        return block()
+    }
+
+    private fun OutputFile.printApi(): String {
+        val text = asText()
+        return text
+            .splitToSequence("\n")
+            .filter {
+                if (it.startsWith("  ")) {
+                    if (it.startsWith("   ")) false
+                    else it[2] != '/' && it[2] != '@'
+                } else {
+                    it == "}" || it.endsWith("{")
+                }
+            }
+            .joinToString(separator = "\n")
+    }
+
+    fun checkApi(src: String, expected: String, dumpClasses: Boolean = false): Unit = ensureSetup {
+        val className = "Test_REPLACEME_${uniqueNumber++}"
+        val fileName = "$className.kt"
+
+        val loader = classLoader("""
+           import androidx.compose.*
+
+           $src
+        """, fileName, dumpClasses)
+
+        val apiString = loader
+            .allGeneratedFiles
+            .filter { it.relativePath.endsWith(".class") }
+            .map { it.printApi() }
+            .map { it.replace('$', '%') }
+            .joinToString(separator = "\n")
+            .replace(className, "Test")
+
+        val expectedApiString = expected
+            .trimIndent()
+            .split("\n")
+            .filter { it.isNotBlank() }
+            .joinToString("\n")
+
+        assertEquals(expectedApiString, apiString)
+    }
+
+    fun checkComposerParam(src: String, dumpClasses: Boolean = false) = ensureSetup {
+        testFile(
+            """
+                import androidx.compose.*
+
+                class FakeNode
+                object FakeNodeApplierAdapter :
+                    ApplyAdapter<FakeNode> {
+                    override fun FakeNode.start(instance: FakeNode) {}
+                    override fun FakeNode.insertAt(index: Int, instance: FakeNode) {}
+                    override fun FakeNode.removeAt(index: Int, count: Int) {}
+                    override fun FakeNode.move(from: Int, to: Int, count: Int) {}
+                    override fun FakeNode.end(instance: FakeNode, parent: FakeNode) {}
+                }
+
+                class FakeComposer(
+                    val root: FakeNode
+                ) : Composer<FakeNode>(
+                    SlotTable(),
+                    Applier(root, FakeNodeApplierAdapter),
+                    Recomposer.current()
+                )
+
+                fun makeComposer(): Composer<*> {
+                    return FakeComposer(FakeNode())
+                }
+
+                fun invokeComposable(composer: Composer<*>, fn: @Composable() () -> Unit) {
+                    val realFn = fn as Function1<Composer<*>, Unit>
+                    realFn(composer)
+                }
+
+                @Composable fun assertComposer(expected: Composer<*>) {
+                    val actual = currentComposerIntrinsic
+                    assert(expected === actual)
+                }
+
+                class Test {
+                  fun test() { run() }
+                }
+                $src
+            """,
+            dumpClasses
+        )
+    }
+
+    fun codegen(text: String, dumpClasses: Boolean = false): Unit = ensureSetup {
+        codegenNoImports(
+            """
+           import android.content.Context
+           import android.widget.*
+           import androidx.compose.*
+
+           $text
+
+        """, dumpClasses)
+    }
+
+    fun codegenNoImports(text: String, dumpClasses: Boolean = false): Unit = ensureSetup {
+        val className = "Test_${uniqueNumber++}"
+        val fileName = "$className.kt"
+
+        classLoader(text, fileName, dumpClasses)
+    }
+}
\ No newline at end of file
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ComposeCallLoweringTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ComposeCallLoweringTests.kt
index 723f700..e6f151a 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ComposeCallLoweringTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ComposeCallLoweringTests.kt
@@ -827,7 +827,7 @@
     fun testComposeWithResult(): Unit = ensureSetup {
         compose(
             """
-                fun <T> identity(block: @Composable() ()->T): T = block()
+                @Composable fun <T> identity(block: @Composable() ()->T): T = block()
 
                 @Composable
                 fun TestCall() {
@@ -1342,7 +1342,7 @@
     @Test
     fun testInline_NonComposable_Identity(): Unit = ensureSetup {
         compose("""
-            inline fun InlineWrapper(base: Int, children: @Composable() ()->Unit) {
+            @Composable inline fun InlineWrapper(base: Int, children: @Composable() ()->Unit) {
               children()
             }
             """,
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ComposerParamSignatureTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ComposerParamSignatureTests.kt
new file mode 100644
index 0000000..faba47d
--- /dev/null
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ComposerParamSignatureTests.kt
@@ -0,0 +1,454 @@
+/*
+ * 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.plugins.kotlin
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+
+@RunWith(ComposeRobolectricTestRunner::class)
+@Config(
+    manifest = Config.NONE,
+    minSdk = 23,
+    maxSdk = 23
+)
+class ComposerParamSignatureTests : AbstractCodegenSignatureTest() {
+
+    @Test
+    fun testCorrectComposerPassed1(): Unit = checkComposerParam(
+        """
+            val a = makeComposer()
+            fun run() {
+                invokeComposable(a) {
+                    assertComposer(a)
+                }
+            }
+        """
+    )
+
+    @Test
+    fun testCorrectComposerPassed2(): Unit = checkComposerParam(
+        """
+            val a = makeComposer()
+            @Composable fun Foo() {
+                assertComposer(a)
+            }
+            fun run() {
+                invokeComposable(a) {
+                    Foo()
+                }
+            }
+        """
+    )
+
+    @Test
+    fun testCorrectComposerPassed3(): Unit = checkComposerParam(
+        """
+            val a = makeComposer()
+            val b = makeComposer()
+            @Composable fun Callback(fn: () -> Unit) {
+                fn()
+            }
+            fun run() {
+                invokeComposable(a) {
+                    assertComposer(a)
+                    Callback {
+                        invokeComposable(b) {
+                            assertComposer(b)
+                        }
+                    }
+                }
+            }
+        """
+    )
+
+    @Test
+    fun testCorrectComposerPassed4(): Unit = checkComposerParam(
+        """
+            val a = makeComposer()
+            val b = makeComposer()
+            @Composable fun makeInt(): Int {
+                assertComposer(a)
+                return 10
+            }
+            @Composable fun WithDefault(x: Int = makeInt()) {}
+            fun run() {
+                invokeComposable(a) {
+                    assertComposer(a)
+                    WithDefault()
+                    WithDefault(10)
+                }
+                invokeComposable(b) {
+                    assertComposer(b)
+                    WithDefault(10)
+                }
+            }
+        """
+    )
+
+    @Test
+    fun testCorrectComposerPassed5(): Unit = checkComposerParam(
+        """
+            val a = makeComposer()
+            @Composable fun Wrap(children: @Composable() () -> Unit) {
+                children()
+            }
+            fun run() {
+                invokeComposable(a) {
+                    assertComposer(a)
+                    Wrap {
+                        assertComposer(a)
+                        Wrap {
+                            assertComposer(a)
+                            Wrap {
+                                assertComposer(a)
+                            }
+                        }
+                    }
+                }
+            }
+        """
+    )
+
+    @Test
+    fun testDefaultParameters(): Unit = checkApi(
+        """
+            @Composable fun Foo(x: Int = 0) {
+
+            }
+        """,
+        """
+            public final class TestKt {
+              public final static Foo(ILandroidx/compose/Composer;)V
+              public static synthetic Foo%default(ILandroidx/compose/Composer;ILjava/lang/Object;)V
+            }
+        """
+    )
+
+    @Test
+    fun testDefaultExpressionsWithComposableCall(): Unit = checkApi(
+        """
+            @Composable fun <T> identity(value: T): T = value
+            @Composable fun Foo(x: Int = identity(20)) {
+
+            }
+            @Composable fun test() {
+                Foo()
+                Foo(10)
+            }
+        """,
+        """
+            public final class TestKt {
+              public final static identity(Ljava/lang/Object;Landroidx/compose/Composer;)Ljava/lang/Object;
+              public final static Foo(ILandroidx/compose/Composer;)V
+              public static synthetic Foo%default(ILandroidx/compose/Composer;ILjava/lang/Object;)V
+              public final static test(Landroidx/compose/Composer;)V
+            }
+        """
+    )
+
+    @Test
+    fun testBasicCallAndParameterUsage(): Unit = checkApi(
+        """
+            @Composable fun Foo(a: Int, b: String) {
+                print(a)
+                print(b)
+                Bar(a, b)
+            }
+
+            @Composable fun Bar(a: Int, b: String) {
+                print(a)
+                print(b)
+            }
+        """,
+        """
+            public final class TestKt {
+              public final static Foo(ILjava/lang/String;Landroidx/compose/Composer;)V
+              public final static Bar(ILjava/lang/String;Landroidx/compose/Composer;)V
+            }
+        """
+    )
+
+    @Test
+    fun testCallFromInlinedLambda(): Unit = checkApi(
+        """
+            @Composable fun Foo() {
+                listOf(1, 2, 3).forEach { Bar(it) }
+            }
+
+            @Composable fun Bar(a: Int) {}
+        """,
+        """
+            public final class TestKt {
+              public final static Foo(Landroidx/compose/Composer;)V
+              public final static Bar(ILandroidx/compose/Composer;)V
+            }
+        """
+    )
+
+    @Test
+    fun testBasicLambda(): Unit = checkApi(
+        """
+            val foo = @Composable { x: Int -> print(x)  }
+            @Composable fun Bar() {
+              foo(123)
+            }
+        """,
+        """
+            public final class TestKt {
+              private final static Lkotlin/jvm/functions/Function2; foo
+              public final static getFoo()Lkotlin/jvm/functions/Function2;
+              public final static Bar(Landroidx/compose/Composer;)V
+              public final static <clinit>()V
+              final static INNERCLASS TestKt%foo%1 null null
+            }
+            final class TestKt%foo%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+              synthetic <init>()V
+              public final invoke(ILandroidx/compose/Composer;)V
+              public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+              final static INNERCLASS TestKt%foo%1 null null
+              OUTERCLASS TestKt <clinit> ()V
+            }
+        """
+    )
+
+    @Test
+    fun testLocalLambda(): Unit = checkApi(
+        """
+            @Composable fun Bar(children: @Composable() () -> Unit) {
+                val foo = @Composable { x: Int -> print(x)  }
+                foo(123)
+                children()
+            }
+        """,
+        """
+            public final class TestKt {
+              public final static Bar(Lkotlin/jvm/functions/Function1;Landroidx/compose/Composer;)V
+              final static INNERCLASS TestKt%Bar%foo%1 null null
+            }
+            final class TestKt%Bar%foo%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+              synthetic <init>()V
+              public final invoke(ILandroidx/compose/Composer;)V
+              public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+              final static INNERCLASS TestKt%Bar%foo%1 null null
+              OUTERCLASS TestKt Bar (Lkotlin/jvm/functions/Function1;Landroidx/compose/Composer;)V
+            }
+        """
+    )
+
+    @Test
+    fun testNesting(): Unit = checkApi(
+        """
+            @Composable fun Wrap(children: @Composable() (x: Int) -> Unit) {
+                children(123)
+            }
+            @Composable fun App(x: Int) {
+                print(x)
+                Wrap { a ->
+                    print(a)
+                    print(x)
+                    Wrap { b ->
+                        print(x)
+                        print(a)
+                        print(b)
+                    }
+                }
+            }
+        """,
+        """
+            public final class TestKt {
+              public final static Wrap(Lkotlin/jvm/functions/Function2;Landroidx/compose/Composer;)V
+              public final static App(ILandroidx/compose/Composer;)V
+              final static INNERCLASS TestKt%App%1 null null
+            }
+            final class TestKt%App%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+              synthetic <init>(I)V
+              public final invoke(ILandroidx/compose/Composer;)V
+              private final synthetic I %x
+              public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+              final static INNERCLASS TestKt%App%1%1 null null
+              final static INNERCLASS TestKt%App%1 null null
+              OUTERCLASS TestKt App (ILandroidx/compose/Composer;)V
+            }
+            final class TestKt%App%1%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+              synthetic <init>(II)V
+              public final invoke(ILandroidx/compose/Composer;)V
+              private final synthetic I %x
+              private final synthetic I %a
+              public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+              final static INNERCLASS TestKt%App%1%1 null null
+              final static INNERCLASS TestKt%App%1 null null
+              OUTERCLASS TestKt%App%1 invoke (ILandroidx/compose/Composer;)V
+            }
+        """
+    )
+
+    @Test
+    fun testComposableInterface(): Unit = checkApi(
+        """
+            interface Foo {
+                @Composable fun bar()
+            }
+
+            class FooImpl : Foo {
+                @Composable override fun bar() {}
+            }
+        """,
+        """
+            public abstract interface Foo {
+              public abstract bar(Landroidx/compose/Composer;)V
+            }
+            public final class FooImpl implements Foo {
+              public <init>()V
+              public bar(Landroidx/compose/Composer;)V
+            }
+        """
+    )
+
+    @Test
+    fun testAbstractComposable(): Unit = checkApi(
+        """
+            abstract class BaseFoo {
+                @Composable abstract fun bar()
+            }
+
+            class FooImpl : BaseFoo() {
+                @Composable override fun bar() {}
+            }
+        """,
+        """
+            public abstract class BaseFoo {
+              public <init>()V
+              public abstract bar(Landroidx/compose/Composer;)V
+            }
+            public final class FooImpl extends BaseFoo {
+              public <init>()V
+              public bar(Landroidx/compose/Composer;)V
+            }
+        """
+    )
+
+    @Test
+    fun testLocalClassAndObjectLiterals(): Unit = checkApi(
+        """
+            @Composable
+            fun Wat() {}
+
+            @Composable
+            fun Foo(x: Int) {
+                Wat()
+                @Composable fun goo() { Wat() }
+                class Bar {
+                    @Composable fun baz() { Wat() }
+                }
+                goo()
+                Bar().baz()
+            }
+        """,
+        """
+            public final class TestKt {
+              public final static Wat(Landroidx/compose/Composer;)V
+              public final static Foo(ILandroidx/compose/Composer;)V
+              private final static Foo%goo(Landroidx/compose/Composer;)V
+              public final static INNERCLASS TestKt%Foo%Bar null Bar
+            }
+            public final class TestKt%Foo%Bar {
+              <init>()V
+              public final baz(Landroidx/compose/Composer;)V
+              public final static INNERCLASS TestKt%Foo%Bar null Bar
+              OUTERCLASS TestKt Foo (ILandroidx/compose/Composer;)V
+            }
+        """
+    )
+
+    @Test
+    fun testNonComposableCode(): Unit = checkApi(
+        """
+            fun A() {}
+            val b: Int get() = 123
+            fun C(x: Int) {
+                var x = 0
+                x++
+
+                class D {
+                    fun E() { A() }
+                    val F: Int get() = 123
+                }
+                val g = object { fun H() {} }
+            }
+            fun I(block: () -> Unit) { block() }
+            fun J() {
+                I {
+                    I {
+                        A()
+                    }
+                }
+            }
+        """,
+        """
+            public final class TestKt {
+              public final static A()V
+              public final static getB()I
+              public final static C(I)V
+              public final static I(Lkotlin/jvm/functions/Function0;)V
+              public final static J()V
+              public final static INNERCLASS TestKt%C%D null D
+              public final static INNERCLASS TestKt%C%g%1 null null
+              final static INNERCLASS TestKt%J%1 null null
+            }
+            public final class TestKt%C%D {
+              <init>()V
+              public final E()V
+              public final getF()I
+              public final static INNERCLASS TestKt%C%D null D
+              OUTERCLASS TestKt C (I)V
+            }
+            public final class TestKt%C%g%1 {
+              <init>()V
+              public final H()V
+              public final static INNERCLASS TestKt%C%g%1 null null
+              OUTERCLASS TestKt C (I)V
+            }
+            final class TestKt%J%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function0 {
+              synthetic <init>()V
+              public final invoke()V
+              public synthetic bridge invoke()Ljava/lang/Object;
+              final static INNERCLASS TestKt%J%1%1 null null
+              final static INNERCLASS TestKt%J%1 null null
+              OUTERCLASS TestKt J ()V
+            }
+            final class TestKt%J%1%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function0 {
+              synthetic <init>()V
+              public final invoke()V
+              public synthetic bridge invoke()Ljava/lang/Object;
+              final static INNERCLASS TestKt%J%1%1 null null
+              final static INNERCLASS TestKt%J%1 null null
+              OUTERCLASS TestKt%J%1 invoke ()V
+            }
+        """
+    )
+
+    override fun setUp() {
+        ComposeFlags.COMPOSER_PARAM = true
+        super.setUp()
+    }
+
+    override fun tearDown() {
+        super.tearDown()
+        ComposeFlags.COMPOSER_PARAM = false
+    }
+}
\ No newline at end of file
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt
index 16289bc..b3645e7 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt
@@ -236,7 +236,7 @@
     fun testComposeWithResult(): Unit = ensureSetup {
         compose(
             """
-                fun <T> identity(block: @Composable() ()->T): T = block()
+                @Composable fun <T> identity(block: @Composable() ()->T): T = block()
 
                 @Composable
                 fun TestCall() {
@@ -793,7 +793,7 @@
     @Test
     fun testInline_NonComposable_Identity(): Unit = ensureSetup {
         compose("""
-            inline fun InlineWrapper(base: Int, children: @Composable() ()->Unit) {
+            @Composable inline fun InlineWrapper(base: Int, children: @Composable() ()->Unit) {
               children()
             }
             """,
@@ -1659,7 +1659,7 @@
     fun testImplicitReceiverPassing1(): Unit = ensureSetup {
         compose(
             """
-                fun Int.Foo(x: @Composable() Int.() -> Unit) {
+                @Composable fun Int.Foo(x: @Composable() Int.() -> Unit) {
                     x()
                 }
             """,
@@ -1681,16 +1681,20 @@
     fun testImplicitReceiverPassing2(): Unit = ensureSetup {
         compose(
             """
-                fun Int.Foo(x: @Composable() Int.(text: String) -> Unit, text: String) {
+                @Composable fun Int.Foo(x: @Composable() Int.(text: String) -> Unit, text: String) {
                     x(text=text)
                 }
+
+                @Composable fun MyText(text: String, id: Int) {
+                    TextView(text=text, id=id)
+                }
             """,
             { mapOf<String, String>() },
             """
                 val id = 42
 
                 id.Foo(text="Hello, world!", x={ text ->
-                    TextView(text=text, id=this)
+                    MyText(text=text, id=this)
                 })
             """
         ).then { activity ->
@@ -2332,7 +2336,7 @@
             var output = ArrayList<String>()
 
             class NotStable { val value = 10 }
-            
+
             @Stable
             class StableClass {
                 override fun equals(other: Any?) = true
@@ -2343,6 +2347,9 @@
               Two
             }
 
+            val mutableStateType = mutableStateOf(1)
+            val stateType: State<Int> = mutableStateType
+
             @Composable
             fun MemoInt(a: Int) {
               output.add("MemoInt a=${'$'}a")
@@ -2378,7 +2385,7 @@
               output.add("MemoEnum")
               Button(text="memo ${'$'}{a}")
             }
-            
+
             @Composable
             fun MemoStable(a: StableClass) {
               output.add("MemoStable")
@@ -2386,6 +2393,18 @@
             }
 
             @Composable
+            fun MemoMutableState(a: MutableState<Int>) {
+              output.add("MemoMutableState")
+              Button(text="memo ${'$'}{a.value}")
+            }
+
+            @Composable
+            fun MemoState(a: State<Int>) {
+              output.add("MemoState")
+              Button(text="memo ${'$'}{a.value}")
+            }
+
+            @Composable
             fun TestSkipping(
                 a: Int,
                 b: Float,
@@ -2393,7 +2412,9 @@
                 d: NotStable,
                 e: ValueHolder,
                 f: EnumState,
-                g: StableClass
+                g: StableClass,
+                h: MutableState<Int>,
+                i: State<Int>
             ) {
               val am = a + m.count
               output.add("TestSkipping a=${'$'}a am=${'$'}am")
@@ -2404,11 +2425,23 @@
               MemoModel(a=e)
               MemoEnum(a=f)
               MemoStable(a=g)
+              MemoMutableState(h)
+              MemoState(i)
             }
 
             @Composable
             fun Main(v: ValueHolder, n: NotStable) {
-              TestSkipping(a=1, b=1f, c=2.0, d=NotStable(), e=v, f=EnumState.One, g=StableClass())
+              TestSkipping(
+                a=1,
+                b=1f,
+                c=2.0,
+                d=NotStable(),
+                e=v,
+                f=EnumState.One,
+                g=StableClass(),
+                h=mutableStateType,
+                i=stateType
+              )
             }
         """, {
             mapOf(
@@ -2422,7 +2455,8 @@
             // Expect that all the methods are called in order
             assertEquals(
                 "TestSkipping a=1 am=1, MemoInt a=1, MemoFloat, " +
-                        "MemoDouble, MemoNotStable, MemoModelHolder, MemoEnum, MemoStable",
+                        "MemoDouble, MemoNotStable, MemoModelHolder, MemoEnum, MemoStable, " +
+                        "MemoMutableState, MemoState",
                 output.joinToString()
             )
             output.clear()
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxModelCodeGenTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxModelCodeGenTests.kt
index b031da3..69db8df 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxModelCodeGenTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxModelCodeGenTests.kt
@@ -306,6 +306,7 @@
 
            class $className {
 
+             @Composable
              fun compose() {
                $composition
              }
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxTransformationTest.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxTransformationTest.kt
index f4a2054..c6839f1 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxTransformationTest.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxTransformationTest.kt
@@ -234,6 +234,7 @@
         """
     )
 
+    // NOTE(lmr): I'm not sure this is a pattern we can support long term...
     fun testFunctionInstanceZeroArgs() = testCompile(
         """
         import androidx.compose.*
@@ -245,6 +246,7 @@
             @Composable
             operator fun invoke() {
                 val foo = object: Function0<Unit> {
+                    @Composable
                     override fun invoke() {
                         Bar()
                     }
@@ -422,6 +424,7 @@
             }
         }
 
+        @Composable
         fun doStuff() {
             val a = A(123)
 
@@ -506,6 +509,7 @@
         @Composable
         fun Simple() {}
 
+        @Composable
         fun run() {
             val foo = @Composable { Simple() }
             foo()
@@ -564,6 +568,7 @@
 
         }
 
+        @Composable
         fun run(text: String) {
             Example {
                 println("hello ${"$"}text")
@@ -609,6 +614,7 @@
         import androidx.compose.*
         import android.widget.TextView
 
+        @Composable
         fun foo() {
             val lambda = @Composable {  }
             for(x in 1..5) {
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ScopeComposabilityTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ScopeComposabilityTests.kt
new file mode 100644
index 0000000..5f640a82
--- /dev/null
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/ScopeComposabilityTests.kt
@@ -0,0 +1,227 @@
+/*
+ * 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.plugins.kotlin
+
+import androidx.compose.plugins.kotlin.analysis.ComposeWritableSlices
+import androidx.compose.plugins.kotlin.ComposableAnnotationChecker.Composability
+import com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.psi.KtElement
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtFunction
+import org.jetbrains.kotlin.psi.KtFunctionLiteral
+import org.jetbrains.kotlin.psi.KtLambdaExpression
+import org.jetbrains.kotlin.psi.KtProperty
+import org.jetbrains.kotlin.psi.KtPropertyAccessor
+import org.jetbrains.kotlin.psi.KtPsiFactory
+import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
+import org.junit.Ignore
+
+class ScopeComposabilityTests : AbstractCodegenTest() {
+
+    fun testNormalFunctions() = assertComposability(
+        """
+            import androidx.compose.*
+
+            fun Foo() {
+                <normal>
+            }
+            class Bar {
+                fun bam() { <normal> }
+                val baz: Int get() { <normal>return 123 }
+            }
+        """
+    )
+
+    fun testPropGetter() = assertComposability(
+        """
+            import androidx.compose.*
+
+            val baz: Int get() { <normal>return 123 }
+        """
+    )
+
+    fun testBasicComposable() = assertComposability(
+        """
+            import androidx.compose.*
+
+            @Composable
+            fun Foo() {
+                <marked>
+            }
+        """
+    )
+
+    fun testBasicComposable2() = assertComposability(
+        """
+            import androidx.compose.*
+
+            val foo = @Composable { <marked> }
+
+            @Composable
+            fun Bar() {
+                <marked>
+                fun bam() { <normal> }
+                val x = { <normal> }
+                val y = @Composable { <marked> }
+                @Composable fun z() { <marked> }
+            }
+        """
+    )
+
+    // TODO(b/147250515): get inlined lambdas to analyze correctly
+    fun xtestBasicComposable3() = assertComposability(
+        """
+            import androidx.compose.*
+
+            @Composable
+            fun Bar() {
+                <marked>
+                listOf(1, 2, 3).forEach {
+                    <inferred> // should be inferred, but is normal
+                }
+            }
+        """
+    )
+
+    fun testBasicComposable4() = assertComposability(
+        """
+            import androidx.compose.*
+
+            @Composable fun Wrap(block: @Composable() () -> Unit) { block() }
+
+            @Composable
+            fun Bar() {
+                <marked>
+                Wrap {
+                    <marked>
+                    Wrap {
+                        <marked>
+                    }
+                }
+            }
+        """
+    )
+
+    fun testBasicComposable5() = assertComposability(
+        """
+            import androidx.compose.*
+
+            @Composable fun Callback(block: () -> Unit) { block() }
+
+            @Composable
+            fun Bar() {
+                <marked>
+                Callback {
+                    <normal>
+                }
+            }
+        """
+    )
+
+    fun testBasicComposable6() = assertComposability(
+        """
+            import androidx.compose.*
+
+            fun kickOff(block: @Composable() () -> Unit) {  }
+
+            fun Bar() {
+                <normal>
+                kickOff {
+                    <marked>
+                }
+            }
+        """
+    )
+
+    private fun <T> setup(block: () -> T): T {
+        return block()
+    }
+
+    fun assertComposability(srcText: String) = setup {
+        val (text, carets) = extractCarets(srcText)
+
+        val environment = myEnvironment ?: error("Environment not initialized")
+
+        val ktFile = KtPsiFactory(environment.project).createFile(text)
+        val bindingContext = JvmResolveUtil.analyze(
+            ktFile,
+            environment
+        ).bindingContext
+
+        carets.forEachIndexed { index, (offset, marking) ->
+            val composability = composabiliityAtOffset(bindingContext, ktFile, offset)
+                ?: error("composability not found for index: $index, offset: $offset. Expected " +
+                        "$marking.")
+
+            when (marking) {
+                "<marked>" -> assertEquals("index: $index", Composability.MARKED, composability)
+                "<inferred>" -> assertEquals("index: $index", Composability.INFERRED, composability)
+                "<normal>" -> assertEquals(
+                    "index: $index",
+                    Composability.NOT_COMPOSABLE,
+                    composability
+                )
+                else -> error("Composability of $marking not recognized.")
+            }
+        }
+    }
+
+    private val callPattern = Regex("(<marked>)|(<inferred>)|(<normal>)")
+    private fun extractCarets(text: String): Pair<String, List<Pair<Int, String>>> {
+        val indices = mutableListOf<Pair<Int, String>>()
+        var offset = 0
+        val src = callPattern.replace(text) {
+            indices.add(it.range.first - offset to it.value)
+            offset += it.range.last - it.range.first + 1
+            ""
+        }
+        return src to indices
+    }
+
+    private fun composabiliityAtOffset(
+        bindingContext: BindingContext,
+        jetFile: KtFile,
+        index: Int
+    ): Composability? {
+        val element = jetFile.findElementAt(index)!!
+        return element.getNearestComposability(bindingContext)
+    }
+}
+
+fun PsiElement?.getNearestComposability(
+    bindingContext: BindingContext
+): Composability? {
+    var node: PsiElement? = this
+    while (node != null) {
+        when (node) {
+            is KtFunctionLiteral -> {
+                // keep going, as this is a "KtFunction", but we actually want the
+                // KtLambdaExpression
+            }
+            is KtLambdaExpression,
+            is KtFunction,
+            is KtPropertyAccessor,
+            is KtProperty -> {
+                val el = node as KtElement
+                return bindingContext.get(ComposeWritableSlices.COMPOSABLE_ANALYSIS, el)
+            }
+        }
+        node = node.parent as? KtElement
+    }
+    return null
+}
\ No newline at end of file
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/frames/FrameDiagnosticTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/frames/FrameDiagnosticTests.kt
index a6d667a..5153f6c 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/frames/FrameDiagnosticTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/frames/FrameDiagnosticTests.kt
@@ -53,4 +53,19 @@
         }
         """
     )
+
+    fun testModel_Report_Nested_Inheritance() = doTest(
+        """
+        import androidx.compose.Model
+
+        open class NonModel { }
+
+        class Tests {
+            @Model
+            class <!UNSUPPORTED_MODEL_INHERITANCE!>MyModel<!> : NonModel() {
+              var strValue = "default"
+            }
+        }
+        """
+    )
 }
\ No newline at end of file
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/frames/FrameTransformExtensionTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/frames/FrameTransformExtensionTests.kt
index 32ab859..1d8a038 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/frames/FrameTransformExtensionTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/frames/FrameTransformExtensionTests.kt
@@ -309,6 +309,33 @@
         }
     """)
 
+    fun testModel_NestedClass() = testFile("""
+        import androidx.compose.Model
+
+        class Test {
+          @Model
+          class MyModel {
+            var value: String = "default"
+          }
+
+          fun test() {
+            val instance = frame { MyModel() }
+            val frame1 = suspended {
+              instance.value = "new value"
+            }
+            frame {
+              instance.value.expectEqual("default")
+            }
+            restored(frame1) {
+              instance.value.expectEqual("new value")
+            }
+            frame {
+              instance.value.expectEqual("new value")
+            }
+          }
+        }
+    """)
+
     override fun helperFiles(): List<KtFile> = listOf(sourceFile("Helpers.kt",
         HELPERS
     ))
@@ -349,11 +376,11 @@
     }
 
     fun Any.expectEqual(expected: Any) {
-      expect(this, expected)
+      expect(expected, this)
     }
 
     fun expect(expected: Any, received: Any) {
       if (expected != received) {
-        throw Exception("Expected ${'$'}expected but received ${'$'}received")
+        throw Exception("Expected \"${'$'}expected\" but received \"${'$'}received\"")
       }
     }"""
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableAnnotationChecker.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableAnnotationChecker.kt
index cb431f7..90495b6 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableAnnotationChecker.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposableAnnotationChecker.kt
@@ -128,8 +128,7 @@
             return candidateDescriptor.type.hasComposableAnnotation()
         }
         if (candidateDescriptor is PropertyDescriptor) {
-            return candidateDescriptor.hasComposableAnnotation() ||
-                    candidateDescriptor.type.hasComposableAnnotation()
+            return candidateDescriptor.hasComposableAnnotation()
         }
         return candidateDescriptor.hasComposableAnnotation()
     }
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFlags.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFlags.kt
index ac6a98f..075eeed 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFlags.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFlags.kt
@@ -19,4 +19,5 @@
 object ComposeFlags {
     var FRAMED_COMPONENTS = false
     var FRAMED_MODEL_CLASSES = true
+    var COMPOSER_PARAM = false
 }
\ No newline at end of file
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt
index 1d30be4..436bc43 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt
@@ -34,9 +34,11 @@
 
 object ComposeFqNames {
     val Composable = ComposeUtils.composeFqName("Composable")
+    val CurrentComposerIntrinsic = ComposeUtils.composeFqName("<get-currentComposerIntrinsic>")
     val Pivotal = ComposeUtils.composeFqName("Pivotal")
     val StableMarker = ComposeUtils.composeFqName("StableMarker")
     val HiddenAttribute = ComposeUtils.composeFqName("HiddenAttribute")
+    val Composer = ComposeUtils.composeFqName("Composer")
     val Package = FqName.fromSegments(listOf("androidx", "compose"))
     val Function0 = FqName.fromSegments(listOf("kotlin", "jvm", "functions", "Function0"))
     fun makeComposableAnnotation(module: ModuleDescriptor): AnnotationDescriptor =
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeIrLoweringExtension.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeIrLoweringExtension.kt
index c766875..8713ad9 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeIrLoweringExtension.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeIrLoweringExtension.kt
@@ -22,6 +22,8 @@
 import org.jetbrains.kotlin.backend.jvm.extensions.IrLoweringExtension
 import androidx.compose.plugins.kotlin.compiler.lower.ComposableCallTransformer
 import androidx.compose.plugins.kotlin.compiler.lower.ComposeObservePatcher
+import androidx.compose.plugins.kotlin.compiler.lower.ComposerIntrinsicTransformer
+import androidx.compose.plugins.kotlin.compiler.lower.ComposerParamTransformer
 import androidx.compose.plugins.kotlin.frames.FrameIrTransformer
 import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase
 import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
@@ -44,10 +46,27 @@
     description = "Rewrite FCS descriptors to IR bytecode"
 )
 
+val ComposerParameterPhase = makeIrModulePhase(
+    ::ComposerParamTransformer,
+    name = "ComposerParameterPhase",
+    description = "Transform @Composable functions to have extra Composer parameter"
+)
+
+val ComposerIntrinsicPhase = makeIrModulePhase(
+    ::ComposerIntrinsicTransformer,
+    name = "ComposerIntrinsicPhase",
+    description = "Replace @Composable intrinsics with their correct values"
+)
+
 class ComposeIrLoweringExtension : IrLoweringExtension {
     override fun interceptLoweringPhases(
         phases: CompilerPhase<JvmBackendContext, IrModuleFragment, IrModuleFragment>
     ): CompilerPhase<JvmBackendContext, IrModuleFragment, IrModuleFragment> {
+        if (ComposeFlags.COMPOSER_PARAM) {
+            return ComposerParameterPhase then
+                    ComposerIntrinsicPhase then
+                    phases
+        }
         return FrameClassGenPhase then
                 ComposeCallPhase then
                 ComposeObservePhase then
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeSyntheticIrExtension.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeSyntheticIrExtension.kt
index dcbb51d..92fc9fe 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeSyntheticIrExtension.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeSyntheticIrExtension.kt
@@ -84,6 +84,7 @@
         statementGenerator: StatementGenerator,
         element: KtCallExpression
     ): IrExpression? {
+        if (ComposeFlags.COMPOSER_PARAM) return null
         val resolvedCall = statementGenerator.getResolvedCall(element)
             ?: return ErrorExpressionGenerator(statementGenerator).generateErrorCall(element)
 
@@ -113,6 +114,7 @@
         statementGenerator: StatementGenerator,
         element: KtSimpleNameExpression
     ): IrExpression? {
+        if (ComposeFlags.COMPOSER_PARAM) return null
         val resolvedCall = statementGenerator.getResolvedCall(element)
             ?: return super.visitSimpleNameExpression(statementGenerator, element)
 
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/KtxNameConventions.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/KtxNameConventions.kt
index 832b72c..7ce6d4dc 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/KtxNameConventions.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/KtxNameConventions.kt
@@ -4,6 +4,8 @@
 
 object KtxNameConventions {
     val COMPOSER = Name.identifier("composer")
+    val COMPOSER_PARAMETER = Name.identifier("\$composer")
+    val CURRENT_COMPOSER_INTRINSIC = Name.special("<get-currentComposerIntrinsic>")
     val EMIT = Name.identifier("emit")
     val CALL = Name.identifier("call")
     val START_EXPR = Name.identifier("startExpr")
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeErrors.java b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeErrors.java
index 1c6b1a5..5ac1306 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeErrors.java
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeErrors.java
@@ -39,7 +39,7 @@
     DiagnosticFactory0<KtElement>
             SUSPEND_FUNCTION_USED_AS_SFC = DiagnosticFactory0.create(ERROR);
     DiagnosticFactory0<PsiElement>
-            COMPOSABLE_INVOCATION_IN_NON_COMPOSABLE = DiagnosticFactory0.create(WARNING);
+            COMPOSABLE_INVOCATION_IN_NON_COMPOSABLE = DiagnosticFactory0.create(ERROR);
     DiagnosticFactory0<KtElement>
             INVALID_TYPE_SIGNATURE_SFC = DiagnosticFactory0.create(ERROR);
     DiagnosticFactory0<KtElement>
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerIntrinsicTransformer.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerIntrinsicTransformer.kt
new file mode 100644
index 0000000..3783949
--- /dev/null
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerIntrinsicTransformer.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.plugins.kotlin.compiler.lower
+
+import androidx.compose.plugins.kotlin.ComposeFqNames
+import org.jetbrains.kotlin.backend.common.FileLoweringPass
+import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
+import org.jetbrains.kotlin.ir.declarations.IrFile
+import org.jetbrains.kotlin.ir.expressions.IrCall
+import org.jetbrains.kotlin.ir.expressions.IrExpression
+import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
+import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
+import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
+
+class ComposerIntrinsicTransformer(val context: JvmBackendContext) :
+    IrElementTransformerVoid(),
+    FileLoweringPass {
+
+    override fun lower(irFile: IrFile) {
+        irFile.transformChildrenVoid(this)
+    }
+
+    override fun visitCall(expression: IrCall): IrExpression {
+        if (expression.descriptor.fqNameSafe == ComposeFqNames.CurrentComposerIntrinsic) {
+            // since this call was transformed by the ComposerParamTransformer, the first argument
+            // to this call is the composer itself. We just replace this expression with the
+            // argument expression and we are good.
+            assert(expression.valueArgumentsCount == 1)
+            val composerExpr = expression.getValueArgument(0)
+            if (composerExpr == null) error("Expected non-null composer argument")
+            return composerExpr
+        }
+        return super.visitCall(expression)
+    }
+}
\ No newline at end of file
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt
new file mode 100644
index 0000000..c4f8080
--- /dev/null
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt
@@ -0,0 +1,425 @@
+/*
+ * 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.plugins.kotlin.compiler.lower
+
+import androidx.compose.plugins.kotlin.ComposableAnnotationChecker
+import androidx.compose.plugins.kotlin.ComposeFqNames
+import androidx.compose.plugins.kotlin.KtxNameConventions
+import org.jetbrains.kotlin.backend.common.FileLoweringPass
+import org.jetbrains.kotlin.backend.common.descriptors.WrappedFunctionDescriptorWithContainerSource
+import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
+import org.jetbrains.kotlin.backend.common.ir.copyTo
+import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
+import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
+import org.jetbrains.kotlin.ir.IrStatement
+import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
+import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
+import org.jetbrains.kotlin.ir.declarations.IrDeclaration
+import org.jetbrains.kotlin.ir.declarations.IrFile
+import org.jetbrains.kotlin.ir.declarations.IrFunction
+import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
+import org.jetbrains.kotlin.ir.declarations.IrTypeParametersContainer
+import org.jetbrains.kotlin.ir.declarations.IrValueParameter
+import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
+import org.jetbrains.kotlin.ir.expressions.IrCall
+import org.jetbrains.kotlin.ir.expressions.IrExpression
+import org.jetbrains.kotlin.ir.expressions.IrGetValue
+import org.jetbrains.kotlin.ir.expressions.IrReturn
+import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
+import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom
+import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
+import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
+import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
+import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
+import org.jetbrains.kotlin.ir.types.IrSimpleType
+import org.jetbrains.kotlin.ir.types.IrType
+import org.jetbrains.kotlin.ir.types.toKotlinType
+import org.jetbrains.kotlin.ir.util.ConstantValueGenerator
+import org.jetbrains.kotlin.ir.util.DeepCopyIrTreeWithSymbols
+import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
+import org.jetbrains.kotlin.ir.util.TypeRemapper
+import org.jetbrains.kotlin.ir.util.TypeTranslator
+import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
+import org.jetbrains.kotlin.ir.util.dump
+import org.jetbrains.kotlin.ir.util.explicitParameters
+import org.jetbrains.kotlin.ir.util.hasAnnotation
+import org.jetbrains.kotlin.ir.util.isFunction
+import org.jetbrains.kotlin.ir.util.patchDeclarationParents
+import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
+import org.jetbrains.kotlin.ir.visitors.acceptVoid
+import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
+import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
+import org.jetbrains.kotlin.name.ClassId
+import org.jetbrains.kotlin.psi.KtFunctionLiteral
+import org.jetbrains.kotlin.psi2ir.findFirstFunction
+import org.jetbrains.kotlin.resolve.inline.InlineUtil
+import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
+import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.types.TypeProjectionImpl
+import org.jetbrains.kotlin.types.replace
+import org.jetbrains.kotlin.util.OperatorNameConventions
+
+private const val DEBUG_LOG = false
+
+class ComposerParamTransformer(val context: JvmBackendContext) :
+    IrElementTransformerVoid(),
+    FileLoweringPass {
+
+    private val transformedFunctions: MutableMap<IrFunction, IrFunction>
+        get() = context.suspendFunctionViews
+
+    private val transformedFunctionSet = mutableSetOf<IrFunction>()
+
+    private val composableChecker = ComposableAnnotationChecker()
+
+    private val typeTranslator =
+        TypeTranslator(
+            context.ir.symbols.externalSymbolTable,
+            context.state.languageVersionSettings,
+            context.builtIns
+        ).apply {
+            constantValueGenerator = ConstantValueGenerator(
+                context.state.module,
+                context.ir.symbols.externalSymbolTable
+            )
+            constantValueGenerator.typeTranslator = this
+        }
+
+    private val builtIns = context.irBuiltIns
+
+    private fun KotlinType.toIrType(): IrType = typeTranslator.translateType(this)
+
+    private val composerTypeDescriptor = context.state.module.findClassAcrossModuleDependencies(
+        ClassId.topLevel(ComposeFqNames.Composer)
+    ) ?: error("Cannot find the Composer class")
+
+    private val composerType = composerTypeDescriptor.defaultType.toIrType()
+
+    override fun lower(irFile: IrFile) {
+        if (DEBUG_LOG) {
+            println("""
+                =========
+                BEFORE
+                =========
+            """.trimIndent())
+            println(irFile.dump())
+        }
+        irFile.transformChildrenVoid(this)
+        if (DEBUG_LOG) {
+            println("""
+                =========
+                AFTER TRANSFORM
+                =========
+            """.trimIndent())
+            println(irFile.dump())
+        }
+        val symbolRemapper = DeepCopySymbolRemapper()
+        irFile.acceptVoid(symbolRemapper)
+        irFile.transformChildren(
+            DeepCopyIrTreeWithSymbols(
+                symbolRemapper,
+                ComposerTypeRemapper(context, typeTranslator, composerTypeDescriptor)
+            ),
+            null
+        )
+        irFile.patchDeclarationParents()
+        if (DEBUG_LOG) {
+            println("""
+                =========
+                AFTER TYPE REMAPPING
+                =========
+            """.trimIndent())
+            println(irFile.dump())
+        }
+    }
+
+    override fun visitFunction(declaration: IrFunction): IrStatement {
+        return super.visitFunction(declaration.withComposerParamIfNeeded())
+    }
+
+    fun IrCall.withComposerParamIfNeeded(composerParam: IrValueParameter): IrCall {
+        val isComposableLambda = isComposableLambdaInvoke()
+        if (!descriptor.isComposable() && !isComposableLambda)
+            return this
+        val ownerFn = when {
+            isComposableLambda ->
+                (symbol.owner as IrSimpleFunction).lambdaInvokeWithComposerParamIfNeeded()
+            else -> (symbol.owner as IrSimpleFunction).withComposerParamIfNeeded()
+        }
+        if (!transformedFunctionSet.contains(ownerFn))
+            return this
+        if (symbol.owner == ownerFn)
+            return this
+        return IrCallImpl(
+            startOffset,
+            endOffset,
+            type,
+            ownerFn.symbol
+        ).also {
+            it.copyTypeArgumentsFrom(this)
+            it.dispatchReceiver = dispatchReceiver
+            it.extensionReceiver = extensionReceiver
+            for (i in 0 until valueArgumentsCount) {
+                it.putValueArgument(i, getValueArgument(i))
+            }
+            it.putValueArgument(
+                valueArgumentsCount,
+                IrGetValueImpl(
+                    UNDEFINED_OFFSET,
+                    UNDEFINED_OFFSET,
+                    composerParam.symbol
+                )
+            )
+        }
+    }
+
+    // Transform `@Composable fun foo(params): RetType` into `fun foo(params, $composer: Composer): RetType`
+    fun IrFunction.withComposerParamIfNeeded(): IrFunction {
+        // don't transform functions that themselves were produced by this function. (ie, if we
+        // call this with a function that has the synthetic composer parameter, we don't want to
+        // transform it further).
+        if (transformedFunctionSet.contains(this)) return this
+
+        // if not a composable fn, nothing we need to do
+        if (!descriptor.isComposable()) return this
+
+        // TODO(lmr): it looks like inlined non-composable lambdas are getting marked as
+        // composable by the ComposableAnnotationChecker. This makes this line a requirement for
+        // two reasons instead of one. We should fix this.
+        if (isInlinedLambda()) return this
+
+        // cache the transformed function with composer parameter
+        return transformedFunctions.getOrPut(this) {
+            copyWithComposerParam().also { transformedFunctionSet.add(it) }
+        }
+    }
+
+    fun IrFunction.lambdaInvokeWithComposerParamIfNeeded(): IrFunction {
+        if (transformedFunctionSet.contains(this)) return this
+        return transformedFunctions.getOrPut(this) {
+            lambdaInvokeWithComposerParam().also { transformedFunctionSet.add(it) }
+        }
+    }
+
+    fun IrFunction.lambdaInvokeWithComposerParam(): IrFunction {
+        val descriptor = descriptor
+        val argCount = descriptor.valueParameters.size
+        val newFnClass = context.irIntrinsics.symbols.getFunction(argCount + 1)
+        val newDescriptor = newFnClass.descriptor.unsubstitutedMemberScope.findFirstFunction(
+            OperatorNameConventions.INVOKE.identifier
+        ) { true }
+
+        return IrFunctionImpl(
+            startOffset,
+            endOffset,
+            origin,
+            newDescriptor,
+            newDescriptor.returnType?.toIrType()!!
+        ).also { fn ->
+            fn.parent = newFnClass.owner
+
+            fn.copyTypeParametersFrom(this)
+            fn.dispatchReceiverParameter = dispatchReceiverParameter?.copyTo(fn)
+            fn.extensionReceiverParameter = extensionReceiverParameter?.copyTo(fn)
+            newDescriptor.valueParameters.forEach { p ->
+                fn.addValueParameter(p.name.identifier, p.type.toIrType())
+            }
+            assert(fn.body == null) { "expected body to be null" }
+        }
+    }
+
+    private fun IrFunction.copy(): IrFunction {
+        // TODO(lmr): use deepCopy instead?
+        val descriptor = descriptor
+
+        val containerSource = (descriptor as? DescriptorWithContainerSource)?.containerSource
+        val newDescriptor = if (containerSource != null)
+                WrappedFunctionDescriptorWithContainerSource(containerSource)
+            else
+                WrappedSimpleFunctionDescriptor(sourceElement = descriptor.source)
+
+        return IrFunctionImpl(
+            startOffset,
+            endOffset,
+            origin,
+            IrSimpleFunctionSymbolImpl(newDescriptor),
+            name,
+            visibility,
+            descriptor.modality,
+            returnType,
+            isInline,
+            isExternal,
+            descriptor.isTailrec,
+            descriptor.isSuspend
+        ).also { fn ->
+            newDescriptor.bind(fn)
+            fn.parent = parent
+            fn.copyTypeParametersFrom(this)
+            fn.dispatchReceiverParameter = dispatchReceiverParameter?.copyTo(fn)
+            fn.extensionReceiverParameter = extensionReceiverParameter?.copyTo(fn)
+            valueParameters.mapTo(fn.valueParameters) { p -> p.copyTo(fn) }
+            fn.body = body?.deepCopyWithSymbols(this)
+        }
+    }
+
+    private fun IrFunction.copyWithComposerParam(): IrFunction {
+        assert(explicitParameters.lastOrNull()?.name != KtxNameConventions.COMPOSER_PARAMETER) {
+            "Attempted to add composer param to $this, but it has already been added."
+        }
+        return copy().also { fn ->
+            val oldFn = this
+            val valueParametersMapping = explicitParameters
+                .zip(fn.explicitParameters)
+                .toMap()
+
+            val composerParam = fn.addValueParameter(
+                KtxNameConventions.COMPOSER_PARAMETER.identifier,
+                composerType
+            )
+            fn.transformChildrenVoid(object : IrElementTransformerVoid() {
+                var isNestedScope = false
+                override fun visitGetValue(expression: IrGetValue): IrGetValue {
+                    val newParam = valueParametersMapping[expression.symbol.owner]
+                    return if (newParam != null) {
+                        IrGetValueImpl(
+                            expression.startOffset,
+                            expression.endOffset,
+                            expression.type,
+                            newParam.symbol,
+                            expression.origin
+                        )
+                    } else expression
+                }
+
+                override fun visitReturn(expression: IrReturn): IrExpression {
+                    if (expression.returnTargetSymbol == oldFn.symbol) {
+                        // update the return statement to point to the new function, or else
+                        // it will be interpreted as a non-local return
+                        return super.visitReturn(IrReturnImpl(
+                            expression.startOffset,
+                            expression.endOffset,
+                            expression.type,
+                            fn.symbol,
+                            expression.value
+                        ))
+                    }
+                    return super.visitReturn(expression)
+                }
+
+                override fun visitFunction(declaration: IrFunction): IrStatement {
+                    val wasNested = isNestedScope
+                    try {
+                        // we don't want to pass the composer parameter in to composable calls
+                        // inside of nested scopes.... *unless* the scope was inlined.
+                        isNestedScope = if (declaration.isInlinedLambda()) wasNested else true
+                        return super.visitFunction(declaration)
+                    } finally {
+                        isNestedScope = wasNested
+                    }
+                }
+
+                override fun visitDeclaration(declaration: IrDeclaration): IrStatement {
+                    if (declaration.parent == oldFn) {
+                        declaration.parent = fn
+                    }
+                    return super.visitDeclaration(declaration)
+                }
+
+                override fun visitCall(expression: IrCall): IrExpression {
+                    val expr = if (!isNestedScope)
+                        expression.withComposerParamIfNeeded(composerParam)
+                    else
+                        expression
+                    return super.visitCall(expr)
+                }
+            })
+        }
+    }
+
+    fun IrCall.isComposableLambdaInvoke(): Boolean {
+        return origin == IrStatementOrigin.INVOKE && dispatchReceiver?.type?.isComposable() == true
+    }
+
+    fun IrFunction.isInlinedLambda(): Boolean {
+        descriptor.findPsi()?.let { psi ->
+            (psi as? KtFunctionLiteral)?.let {
+                if (InlineUtil.isInlinedArgument(
+                        it,
+                        context.state.bindingContext,
+                        false
+                    )
+                )
+                    return true
+            }
+        }
+        return false
+    }
+
+    fun FunctionDescriptor.isComposable(): Boolean {
+        val composability = composableChecker.analyze(context.state.bindingTrace, this)
+        return when (composability) {
+            ComposableAnnotationChecker.Composability.NOT_COMPOSABLE -> false
+            ComposableAnnotationChecker.Composability.MARKED -> true
+            ComposableAnnotationChecker.Composability.INFERRED -> true
+        }
+    }
+
+    fun IrType.isComposable(): Boolean {
+        return annotations.hasAnnotation(ComposeFqNames.Composable)
+    }
+}
+
+class ComposerTypeRemapper(
+    private val context: JvmBackendContext,
+    private val typeTranslator: TypeTranslator,
+    private val composerTypeDescriptor: ClassDescriptor
+) : TypeRemapper {
+    override fun enterScope(irTypeParametersContainer: IrTypeParametersContainer) {}
+
+    override fun leaveScope() {}
+
+    private fun IrType.isComposable(): Boolean {
+        return annotations.hasAnnotation(ComposeFqNames.Composable)
+    }
+
+    private fun KotlinType.toIrType(): IrType = typeTranslator.translateType(this)
+
+    override fun remapType(type: IrType): IrType {
+        // TODO(lmr):
+        // This is basically creating the KotlinType and then converting to an IrType. Consider
+        // rewriting to just create the IrType directly, which would probably be more efficient.
+        if (type !is IrSimpleType) return type
+        if (!type.isFunction()) return type
+        if (!type.isComposable()) return type
+        val oldArguments = type.toKotlinType().arguments
+        val newArguments =
+            oldArguments.subList(0, oldArguments.size - 1) +
+                    TypeProjectionImpl(composerTypeDescriptor.defaultType) +
+                    oldArguments.last()
+
+        return context
+            .irBuiltIns
+            .builtIns
+            .getFunction(oldArguments.size) // return type is an argument, so this is n + 1
+            .defaultType
+            .replace(newArguments)
+            .toIrType()
+    }
+}
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/frames/analysis/FramePackageAnalysisHandlerExtension.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/frames/analysis/FramePackageAnalysisHandlerExtension.kt
index 9b70b31..7f4de3e 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/frames/analysis/FramePackageAnalysisHandlerExtension.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/frames/analysis/FramePackageAnalysisHandlerExtension.kt
@@ -8,76 +8,82 @@
 import org.jetbrains.kotlin.descriptors.ClassDescriptor
 import org.jetbrains.kotlin.descriptors.ModuleDescriptor
 import org.jetbrains.kotlin.descriptors.annotations.Annotated
-import org.jetbrains.kotlin.diagnostics.reportFromPlugin
 import org.jetbrains.kotlin.name.Name
 import org.jetbrains.kotlin.psi.KtClassOrObject
 import org.jetbrains.kotlin.psi.KtFile
 import androidx.compose.plugins.kotlin.ComposeFlags
-import androidx.compose.plugins.kotlin.analysis.ComposeDefaultErrorMessages
-import androidx.compose.plugins.kotlin.analysis.ComposeErrors
 import androidx.compose.plugins.kotlin.frames.FrameRecordClassDescriptor
 import androidx.compose.plugins.kotlin.frames.SyntheticFramePackageDescriptor
 import androidx.compose.plugins.kotlin.frames.abstractRecordClassName
 import androidx.compose.plugins.kotlin.frames.findTopLevel
 import androidx.compose.plugins.kotlin.frames.modelClassName
 import androidx.compose.plugins.kotlin.frames.recordClassName
+import org.jetbrains.kotlin.psi.KtDeclaration
 import org.jetbrains.kotlin.resolve.BindingTrace
 import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
 import org.jetbrains.kotlin.resolve.lazy.ResolveSession
-import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
-import org.jetbrains.kotlin.types.typeUtil.isInterface
+
+private fun doAnalysis(
+    module: ModuleDescriptor,
+    bindingTrace: BindingTrace,
+    files: Collection<KtFile>,
+    resolveSession: ResolveSession
+) {
+    if (!ComposeFlags.FRAMED_MODEL_CLASSES) return
+    for (file in files) {
+        analyseDeclarations(module, bindingTrace, file.declarations, resolveSession)
+    }
+}
+
+private fun analyseDeclarations(
+    module: ModuleDescriptor,
+    bindingTrace: BindingTrace,
+    declarations: List<KtDeclaration>,
+    resolveSession: ResolveSession
+) {
+    for (declaration in declarations) {
+        val ktClass = declaration as? KtClassOrObject ?: continue
+
+        analyseDeclarations(module, bindingTrace, ktClass.declarations, resolveSession)
+
+        val framedDescriptor = resolveSession.resolveToDescriptor(declaration) as?
+                ClassDescriptor ?: continue
+        if (!framedDescriptor.hasModelAnnotation()) continue
+
+        val classFqName = ktClass.fqName!!
+        val recordFqName = classFqName.parent().child(Name.identifier(
+            "${classFqName.shortName()}\$Record")
+        )
+        val recordSimpleName = recordFqName.shortName()
+        val recordPackage =
+            SyntheticFramePackageDescriptor(
+                module,
+                recordFqName.parent()
+            )
+        val baseTypeDescriptor = module.findTopLevel(abstractRecordClassName)
+        val recordDescriptor = module.findTopLevel(recordClassName)
+        val baseType = baseTypeDescriptor.defaultType
+        val frameClass =
+            FrameRecordClassDescriptor(
+                recordSimpleName,
+                recordPackage,
+                recordDescriptor,
+                framedDescriptor,
+                listOf(baseType),
+                bindingTrace.bindingContext
+            )
+
+        recordPackage.setClassDescriptor(frameClass)
+        bindingTrace.record(FrameWritableSlices.RECORD_CLASS, classFqName, frameClass)
+        bindingTrace.record(
+            FrameWritableSlices.FRAMED_DESCRIPTOR,
+            classFqName,
+            framedDescriptor
+        )
+    }
+}
 
 class FramePackageAnalysisHandlerExtension : AnalysisHandlerExtension {
-    companion object {
-        fun doAnalysis(
-            module: ModuleDescriptor,
-            bindingTrace: BindingTrace,
-            files: Collection<KtFile>,
-            resolveSession: ResolveSession
-        ) {
-            if (!ComposeFlags.FRAMED_MODEL_CLASSES) return
-            for (file in files) {
-                for (declaration in file.declarations) {
-                    val ktClass = declaration as? KtClassOrObject ?: continue
-                    val framedDescriptor = resolveSession.resolveToDescriptor(declaration) as?
-                            ClassDescriptor ?: continue
-                    if (!framedDescriptor.hasModelAnnotation()) continue
-
-                    val classFqName = ktClass.fqName!!
-                    val recordFqName = classFqName.parent().child(Name.identifier(
-                        "${classFqName.shortName()}\$Record")
-                    )
-                    val recordSimpleName = recordFqName.shortName()
-                    val recordPackage =
-                        SyntheticFramePackageDescriptor(
-                            module,
-                            recordFqName.parent()
-                        )
-                    val baseTypeDescriptor = module.findTopLevel(abstractRecordClassName)
-                    val recordDescriptor = module.findTopLevel(recordClassName)
-                    val baseType = baseTypeDescriptor.defaultType
-                    val frameClass =
-                        FrameRecordClassDescriptor(
-                            recordSimpleName,
-                            recordPackage,
-                            recordDescriptor,
-                            framedDescriptor,
-                            listOf(baseType),
-                            bindingTrace.bindingContext
-                        )
-
-                    recordPackage.setClassDescriptor(frameClass)
-                    bindingTrace.record(FrameWritableSlices.RECORD_CLASS, classFqName, frameClass)
-                    bindingTrace.record(
-                        FrameWritableSlices.FRAMED_DESCRIPTOR,
-                        classFqName,
-                        framedDescriptor
-                    )
-                }
-            }
-        }
-    }
-
     override fun doAnalysis(
         project: Project,
         module: ModuleDescriptor,
diff --git a/compose/compose-runtime/api/0.1.0-dev04.txt b/compose/compose-runtime/api/0.1.0-dev04.txt
index 26ce282..e48b293 100644
--- a/compose/compose-runtime/api/0.1.0-dev04.txt
+++ b/compose/compose-runtime/api/0.1.0-dev04.txt
@@ -126,6 +126,7 @@
   public final class ComposerKt {
     method public static inline <N, T> T! cache(androidx.compose.Composer<N>, boolean valid = true, kotlin.jvm.functions.Function0<? extends T> block);
     method public static inline <T> T! escapeCompose(kotlin.jvm.functions.Function1<? super androidx.compose.NullCompilationScope,? extends T> block);
+    method public static androidx.compose.Composer<?> getCurrentComposerIntrinsic();
     method public static <N> Object? nextValue(androidx.compose.Composer<N>);
     field public static final int DEFAULT_SLOT_ACTIONS_SIZE = 16; // 0x10
     field public static final int DEFAULT_SLOT_KEYS_SIZE = 8; // 0x8
@@ -248,7 +249,7 @@
   @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Model {
   }
 
-  public interface MutableState<T> extends androidx.compose.State<T> {
+  @androidx.compose.Stable public interface MutableState<T> extends androidx.compose.State<T> {
     method public operator T! component1();
     method public operator kotlin.jvm.functions.Function1<T,kotlin.Unit> component2();
     method public operator T! getValue(Object? thisObj, kotlin.reflect.KProperty<?> property);
@@ -424,7 +425,7 @@
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
   }
 
-  public interface State<T> {
+  @androidx.compose.Stable public interface State<T> {
     method public T! getValue();
     property public abstract T! value;
   }
diff --git a/compose/compose-runtime/api/api_lint.ignore b/compose/compose-runtime/api/api_lint.ignore
index 0d0d66e..b95542f 100644
--- a/compose/compose-runtime/api/api_lint.ignore
+++ b/compose/compose-runtime/api/api_lint.ignore
@@ -35,6 +35,8 @@
     Context is distinct, so it must be the first argument (method `ViewComposer`)
 
 
+DocumentExceptions: androidx.compose.ComposerKt#getCurrentComposerIntrinsic():
+    Method ComposerKt.getCurrentComposerIntrinsic appears to be throwing kotlin.NotImplementedError; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
 DocumentExceptions: androidx.compose.frames.FramesKt#commit(androidx.compose.frames.Frame):
     Method FramesKt.commit appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
 DocumentExceptions: androidx.compose.frames.FramesKt#currentFrame():
diff --git a/compose/compose-runtime/api/current.txt b/compose/compose-runtime/api/current.txt
index 26ce282..e48b293 100644
--- a/compose/compose-runtime/api/current.txt
+++ b/compose/compose-runtime/api/current.txt
@@ -126,6 +126,7 @@
   public final class ComposerKt {
     method public static inline <N, T> T! cache(androidx.compose.Composer<N>, boolean valid = true, kotlin.jvm.functions.Function0<? extends T> block);
     method public static inline <T> T! escapeCompose(kotlin.jvm.functions.Function1<? super androidx.compose.NullCompilationScope,? extends T> block);
+    method public static androidx.compose.Composer<?> getCurrentComposerIntrinsic();
     method public static <N> Object? nextValue(androidx.compose.Composer<N>);
     field public static final int DEFAULT_SLOT_ACTIONS_SIZE = 16; // 0x10
     field public static final int DEFAULT_SLOT_KEYS_SIZE = 8; // 0x8
@@ -248,7 +249,7 @@
   @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Model {
   }
 
-  public interface MutableState<T> extends androidx.compose.State<T> {
+  @androidx.compose.Stable public interface MutableState<T> extends androidx.compose.State<T> {
     method public operator T! component1();
     method public operator kotlin.jvm.functions.Function1<T,kotlin.Unit> component2();
     method public operator T! getValue(Object? thisObj, kotlin.reflect.KProperty<?> property);
@@ -424,7 +425,7 @@
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
   }
 
-  public interface State<T> {
+  @androidx.compose.Stable public interface State<T> {
     method public T! getValue();
     property public abstract T! value;
   }
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev04.txt b/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev04.txt
index 26ce282..e48b293 100644
--- a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev04.txt
+++ b/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev04.txt
@@ -126,6 +126,7 @@
   public final class ComposerKt {
     method public static inline <N, T> T! cache(androidx.compose.Composer<N>, boolean valid = true, kotlin.jvm.functions.Function0<? extends T> block);
     method public static inline <T> T! escapeCompose(kotlin.jvm.functions.Function1<? super androidx.compose.NullCompilationScope,? extends T> block);
+    method public static androidx.compose.Composer<?> getCurrentComposerIntrinsic();
     method public static <N> Object? nextValue(androidx.compose.Composer<N>);
     field public static final int DEFAULT_SLOT_ACTIONS_SIZE = 16; // 0x10
     field public static final int DEFAULT_SLOT_KEYS_SIZE = 8; // 0x8
@@ -248,7 +249,7 @@
   @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Model {
   }
 
-  public interface MutableState<T> extends androidx.compose.State<T> {
+  @androidx.compose.Stable public interface MutableState<T> extends androidx.compose.State<T> {
     method public operator T! component1();
     method public operator kotlin.jvm.functions.Function1<T,kotlin.Unit> component2();
     method public operator T! getValue(Object? thisObj, kotlin.reflect.KProperty<?> property);
@@ -424,7 +425,7 @@
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
   }
 
-  public interface State<T> {
+  @androidx.compose.Stable public interface State<T> {
     method public T! getValue();
     property public abstract T! value;
   }
diff --git a/compose/compose-runtime/api/public_plus_experimental_current.txt b/compose/compose-runtime/api/public_plus_experimental_current.txt
index 26ce282..e48b293 100644
--- a/compose/compose-runtime/api/public_plus_experimental_current.txt
+++ b/compose/compose-runtime/api/public_plus_experimental_current.txt
@@ -126,6 +126,7 @@
   public final class ComposerKt {
     method public static inline <N, T> T! cache(androidx.compose.Composer<N>, boolean valid = true, kotlin.jvm.functions.Function0<? extends T> block);
     method public static inline <T> T! escapeCompose(kotlin.jvm.functions.Function1<? super androidx.compose.NullCompilationScope,? extends T> block);
+    method public static androidx.compose.Composer<?> getCurrentComposerIntrinsic();
     method public static <N> Object? nextValue(androidx.compose.Composer<N>);
     field public static final int DEFAULT_SLOT_ACTIONS_SIZE = 16; // 0x10
     field public static final int DEFAULT_SLOT_KEYS_SIZE = 8; // 0x8
@@ -248,7 +249,7 @@
   @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Model {
   }
 
-  public interface MutableState<T> extends androidx.compose.State<T> {
+  @androidx.compose.Stable public interface MutableState<T> extends androidx.compose.State<T> {
     method public operator T! component1();
     method public operator kotlin.jvm.functions.Function1<T,kotlin.Unit> component2();
     method public operator T! getValue(Object? thisObj, kotlin.reflect.KProperty<?> property);
@@ -424,7 +425,7 @@
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
   }
 
-  public interface State<T> {
+  @androidx.compose.Stable public interface State<T> {
     method public T! getValue();
     property public abstract T! value;
   }
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev04.txt b/compose/compose-runtime/api/restricted_0.1.0-dev04.txt
index 26ce282..e48b293 100644
--- a/compose/compose-runtime/api/restricted_0.1.0-dev04.txt
+++ b/compose/compose-runtime/api/restricted_0.1.0-dev04.txt
@@ -126,6 +126,7 @@
   public final class ComposerKt {
     method public static inline <N, T> T! cache(androidx.compose.Composer<N>, boolean valid = true, kotlin.jvm.functions.Function0<? extends T> block);
     method public static inline <T> T! escapeCompose(kotlin.jvm.functions.Function1<? super androidx.compose.NullCompilationScope,? extends T> block);
+    method public static androidx.compose.Composer<?> getCurrentComposerIntrinsic();
     method public static <N> Object? nextValue(androidx.compose.Composer<N>);
     field public static final int DEFAULT_SLOT_ACTIONS_SIZE = 16; // 0x10
     field public static final int DEFAULT_SLOT_KEYS_SIZE = 8; // 0x8
@@ -248,7 +249,7 @@
   @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Model {
   }
 
-  public interface MutableState<T> extends androidx.compose.State<T> {
+  @androidx.compose.Stable public interface MutableState<T> extends androidx.compose.State<T> {
     method public operator T! component1();
     method public operator kotlin.jvm.functions.Function1<T,kotlin.Unit> component2();
     method public operator T! getValue(Object? thisObj, kotlin.reflect.KProperty<?> property);
@@ -424,7 +425,7 @@
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
   }
 
-  public interface State<T> {
+  @androidx.compose.Stable public interface State<T> {
     method public T! getValue();
     property public abstract T! value;
   }
diff --git a/compose/compose-runtime/api/restricted_current.txt b/compose/compose-runtime/api/restricted_current.txt
index 26ce282..e48b293 100644
--- a/compose/compose-runtime/api/restricted_current.txt
+++ b/compose/compose-runtime/api/restricted_current.txt
@@ -126,6 +126,7 @@
   public final class ComposerKt {
     method public static inline <N, T> T! cache(androidx.compose.Composer<N>, boolean valid = true, kotlin.jvm.functions.Function0<? extends T> block);
     method public static inline <T> T! escapeCompose(kotlin.jvm.functions.Function1<? super androidx.compose.NullCompilationScope,? extends T> block);
+    method public static androidx.compose.Composer<?> getCurrentComposerIntrinsic();
     method public static <N> Object? nextValue(androidx.compose.Composer<N>);
     field public static final int DEFAULT_SLOT_ACTIONS_SIZE = 16; // 0x10
     field public static final int DEFAULT_SLOT_KEYS_SIZE = 8; // 0x8
@@ -248,7 +249,7 @@
   @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Model {
   }
 
-  public interface MutableState<T> extends androidx.compose.State<T> {
+  @androidx.compose.Stable public interface MutableState<T> extends androidx.compose.State<T> {
     method public operator T! component1();
     method public operator kotlin.jvm.functions.Function1<T,kotlin.Unit> component2();
     method public operator T! getValue(Object? thisObj, kotlin.reflect.KProperty<?> property);
@@ -424,7 +425,7 @@
   @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
   }
 
-  public interface State<T> {
+  @androidx.compose.Stable public interface State<T> {
     method public T! getValue();
     property public abstract T! value;
   }
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Composer.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Composer.kt
index 3d80e6d..d166ea4 100644
--- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Composer.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Composer.kt
@@ -1748,3 +1748,8 @@
 }
 
 inline fun <T> escapeCompose(block: NullCompilationScope.() -> T) = NullCompilationScope.block()
+
+@Composable
+val currentComposerIntrinsic: Composer<*> get() {
+    throw NotImplementedError("Implemented as an intrinsic")
+}
\ No newline at end of file
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/MutableState.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/MutableState.kt
index 2c9c078..081d83e 100644
--- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/MutableState.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/MutableState.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose
 
-import androidx.compose.annotations.Hide
 import androidx.compose.frames.AbstractRecord
 import androidx.compose.frames.Framed
 import androidx.compose.frames.Record
@@ -132,6 +131,7 @@
  * @see [MutableState]
  * @see [mutableStateOf]
  */
+@Stable
 interface State<T> {
     val value: T
 }
@@ -146,6 +146,7 @@
  * @see [State]
  * @see [mutableStateOf]
  */
+@Stable
 interface MutableState<T> : State<T> {
     override var value: T
     operator fun component1(): T
diff --git a/core/OWNERS b/core/OWNERS
index 914e9ca..6a3338bc 100644
--- a/core/OWNERS
+++ b/core/OWNERS
@@ -8,6 +8,7 @@
 jaggies@google.com
 joshmccloskey@google.com
 kchyn@google.com
+curtislb@google.com
 
 # For text related files
 nona@google.com
diff --git a/core/core/src/main/java/androidx/core/app/RemoteInput.java b/core/core/src/main/java/androidx/core/app/RemoteInput.java
index 3bf4d4d..dd01c0b 100644
--- a/core/core/src/main/java/androidx/core/app/RemoteInput.java
+++ b/core/core/src/main/java/androidx/core/app/RemoteInput.java
@@ -580,7 +580,7 @@
         if (!clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
             return null;
         }
-        if (!clipDescription.getLabel().equals(RemoteInput.RESULTS_CLIP_LABEL)) {
+        if (!clipDescription.getLabel().toString().contentEquals(RemoteInput.RESULTS_CLIP_LABEL)) {
             return null;
         }
         return clipData.getItemAt(0).getIntent();
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 0a8e2a9f1..421c557 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -1603,6 +1603,10 @@
      * searches through fragments that are currently added to the manager's
      * activity; if no such fragment is found, then all fragments currently
      * on the back stack are searched.
+     * <p>
+     * If provided a {@code null} tag, this method returns null.
+     *
+     * @param tag the tag used to search for the fragment
      * @return The fragment if found or null otherwise.
      */
     @Nullable
diff --git a/gradle.properties b/gradle.properties
index aecea02..94c027d 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,4 @@
-org.gradle.jvmargs=-Xmx8g -XX:MaxPermSize=8g
+org.gradle.jvmargs=-Xmx8g
 org.gradle.daemon=true
 org.gradle.configureondemand=true
 org.gradle.parallel=true
@@ -14,4 +14,4 @@
 android.useNewJarCreator=false
 # Workaround for b/141364941
 android.forceJacocoOutOfProcess=true
-android.namespacedRClass=true
\ No newline at end of file
+android.namespacedRClass=true
diff --git a/gradlew b/gradlew
index 6233fe3..243699d 100755
--- a/gradlew
+++ b/gradlew
@@ -8,22 +8,27 @@
 
 # --------- androidx specific code needed for build server. ------------------
 
+SCRIPT_PATH="$(cd $(dirname $0) && pwd)"
 if [ -n "$OUT_DIR" ] ; then
     mkdir -p "$OUT_DIR"
     OUT_DIR="$(cd $OUT_DIR && pwd)"
     export GRADLE_USER_HOME="$OUT_DIR/.gradle"
     export TMPDIR=$OUT_DIR
 else
-    SCRIPT_PATH="$(cd $(dirname $0) && pwd)"
     CHECKOUT_ROOT="$(cd $SCRIPT_PATH/../.. && pwd)"
     export OUT_DIR="$CHECKOUT_ROOT/out"
 fi
 
+XMX_ARG="$(cd $SCRIPT_PATH && grep org.gradle.jvmargs gradle.properties | sed 's/^/-D/')"
 if [ -n "$DIST_DIR" ]; then
     mkdir -p "$DIST_DIR"
     DIST_DIR="$(cd $DIST_DIR && pwd)"
     export LINT_PRINT_STACKTRACE=true
 
+    #Set the initial heap size to match the max heap size,
+    #by replacing a string like "-Xmx1g" with one like "-Xms1g -Xmx1g"
+    XMX_ARG="$(echo $XMX_ARG | sed 's/-Xmx\([^ ]*\)/-Xms\1 -Xmx\1/')"
+
     # We don't set a default DIST_DIR in an else clause here because Studio doesn't use gradlew
     # and doesn't set DIST_DIR and we want gradlew and Studio to match
 fi
@@ -210,7 +215,7 @@
   TMPDIR_ARG="-Djava.io.tmpdir=$TMPDIR"
 fi
 
-if "$JAVACMD" "${JVM_OPTS[@]}" $TMPDIR_ARG -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain $HOME_SYSTEM_PROPERTY_ARGUMENT $TMPDIR_ARG "$@"; then
+if "$JAVACMD" "${JVM_OPTS[@]}" $TMPDIR_ARG -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain $HOME_SYSTEM_PROPERTY_ARGUMENT $TMPDIR_ARG "$XMX_ARG" "$@"; then
   exit 0
 else
   # Print AndroidX-specific help message if build fails
diff --git a/interpolator/api/1.0.0.txt b/interpolator/interpolator/api/1.0.0.txt
similarity index 100%
rename from interpolator/api/1.0.0.txt
rename to interpolator/interpolator/api/1.0.0.txt
diff --git a/interpolator/api/1.1.0-alpha01.txt b/interpolator/interpolator/api/1.1.0-alpha01.txt
similarity index 100%
rename from interpolator/api/1.1.0-alpha01.txt
rename to interpolator/interpolator/api/1.1.0-alpha01.txt
diff --git a/interpolator/api/current.txt b/interpolator/interpolator/api/current.txt
similarity index 100%
rename from interpolator/api/current.txt
rename to interpolator/interpolator/api/current.txt
diff --git a/interpolator/api/public_plus_experimental_1.0.0.txt b/interpolator/interpolator/api/public_plus_experimental_1.0.0.txt
similarity index 100%
rename from interpolator/api/public_plus_experimental_1.0.0.txt
rename to interpolator/interpolator/api/public_plus_experimental_1.0.0.txt
diff --git a/interpolator/api/public_plus_experimental_1.1.0-alpha01.txt b/interpolator/interpolator/api/public_plus_experimental_1.1.0-alpha01.txt
similarity index 100%
rename from interpolator/api/public_plus_experimental_1.1.0-alpha01.txt
rename to interpolator/interpolator/api/public_plus_experimental_1.1.0-alpha01.txt
diff --git a/interpolator/api/public_plus_experimental_current.txt b/interpolator/interpolator/api/public_plus_experimental_current.txt
similarity index 100%
rename from interpolator/api/public_plus_experimental_current.txt
rename to interpolator/interpolator/api/public_plus_experimental_current.txt
diff --git a/interpolator/api/res-1.0.0.txt b/interpolator/interpolator/api/res-1.0.0.txt
similarity index 100%
rename from interpolator/api/res-1.0.0.txt
rename to interpolator/interpolator/api/res-1.0.0.txt
diff --git a/interpolator/api/res-1.1.0-alpha01.txt b/interpolator/interpolator/api/res-1.1.0-alpha01.txt
similarity index 100%
rename from interpolator/api/res-1.1.0-alpha01.txt
rename to interpolator/interpolator/api/res-1.1.0-alpha01.txt
diff --git a/interpolator/api/restricted_1.0.0.txt b/interpolator/interpolator/api/restricted_1.0.0.txt
similarity index 100%
rename from interpolator/api/restricted_1.0.0.txt
rename to interpolator/interpolator/api/restricted_1.0.0.txt
diff --git a/interpolator/api/restricted_1.1.0-alpha01.txt b/interpolator/interpolator/api/restricted_1.1.0-alpha01.txt
similarity index 100%
rename from interpolator/api/restricted_1.1.0-alpha01.txt
rename to interpolator/interpolator/api/restricted_1.1.0-alpha01.txt
diff --git a/interpolator/api/restricted_current.txt b/interpolator/interpolator/api/restricted_current.txt
similarity index 100%
rename from interpolator/api/restricted_current.txt
rename to interpolator/interpolator/api/restricted_current.txt
diff --git a/interpolator/api_legacy/0.0.0.txt b/interpolator/interpolator/api_legacy/0.0.0.txt
similarity index 100%
rename from interpolator/api_legacy/0.0.0.txt
rename to interpolator/interpolator/api_legacy/0.0.0.txt
diff --git a/interpolator/api_legacy/28.0.0-alpha1.txt b/interpolator/interpolator/api_legacy/28.0.0-alpha1.txt
similarity index 100%
rename from interpolator/api_legacy/28.0.0-alpha1.txt
rename to interpolator/interpolator/api_legacy/28.0.0-alpha1.txt
diff --git a/interpolator/api_legacy/current.txt b/interpolator/interpolator/api_legacy/current.txt
similarity index 100%
rename from interpolator/api_legacy/current.txt
rename to interpolator/interpolator/api_legacy/current.txt
diff --git a/interpolator/build.gradle b/interpolator/interpolator/build.gradle
similarity index 100%
rename from interpolator/build.gradle
rename to interpolator/interpolator/build.gradle
diff --git a/interpolator/src/main/AndroidManifest.xml b/interpolator/interpolator/src/main/AndroidManifest.xml
similarity index 100%
rename from interpolator/src/main/AndroidManifest.xml
rename to interpolator/interpolator/src/main/AndroidManifest.xml
diff --git a/interpolator/src/main/java/androidx/interpolator/view/animation/FastOutLinearInInterpolator.java b/interpolator/interpolator/src/main/java/androidx/interpolator/view/animation/FastOutLinearInInterpolator.java
similarity index 100%
rename from interpolator/src/main/java/androidx/interpolator/view/animation/FastOutLinearInInterpolator.java
rename to interpolator/interpolator/src/main/java/androidx/interpolator/view/animation/FastOutLinearInInterpolator.java
diff --git a/interpolator/src/main/java/androidx/interpolator/view/animation/FastOutSlowInInterpolator.java b/interpolator/interpolator/src/main/java/androidx/interpolator/view/animation/FastOutSlowInInterpolator.java
similarity index 100%
rename from interpolator/src/main/java/androidx/interpolator/view/animation/FastOutSlowInInterpolator.java
rename to interpolator/interpolator/src/main/java/androidx/interpolator/view/animation/FastOutSlowInInterpolator.java
diff --git a/interpolator/src/main/java/androidx/interpolator/view/animation/LinearOutSlowInInterpolator.java b/interpolator/interpolator/src/main/java/androidx/interpolator/view/animation/LinearOutSlowInInterpolator.java
similarity index 100%
rename from interpolator/src/main/java/androidx/interpolator/view/animation/LinearOutSlowInInterpolator.java
rename to interpolator/interpolator/src/main/java/androidx/interpolator/view/animation/LinearOutSlowInInterpolator.java
diff --git a/interpolator/src/main/java/androidx/interpolator/view/animation/LookupTableInterpolator.java b/interpolator/interpolator/src/main/java/androidx/interpolator/view/animation/LookupTableInterpolator.java
similarity index 100%
rename from interpolator/src/main/java/androidx/interpolator/view/animation/LookupTableInterpolator.java
rename to interpolator/interpolator/src/main/java/androidx/interpolator/view/animation/LookupTableInterpolator.java
diff --git a/leanback-preference/src/main/java/androidx/leanback/preference/LeanbackListPreferenceDialogFragment.java b/leanback-preference/src/main/java/androidx/leanback/preference/LeanbackListPreferenceDialogFragment.java
index 4d7e852..5f774ae 100644
--- a/leanback-preference/src/main/java/androidx/leanback/preference/LeanbackListPreferenceDialogFragment.java
+++ b/leanback-preference/src/main/java/androidx/leanback/preference/LeanbackListPreferenceDialogFragment.java
@@ -225,7 +225,8 @@
 
         @Override
         public void onBindViewHolder(ViewHolder holder, int position) {
-            holder.getWidgetView().setChecked(mEntryValues[position].equals(mSelectedValue));
+            holder.getWidgetView().setChecked(
+                    TextUtils.equals(mEntryValues[position].toString(), mSelectedValue));
             holder.getTitleView().setText(mEntries[position]);
         }
 
diff --git a/leanback-preference/src/main/java/androidx/leanback/preference/LeanbackListPreferenceDialogFragmentCompat.java b/leanback-preference/src/main/java/androidx/leanback/preference/LeanbackListPreferenceDialogFragmentCompat.java
index 941587d..88fd21d 100644
--- a/leanback-preference/src/main/java/androidx/leanback/preference/LeanbackListPreferenceDialogFragmentCompat.java
+++ b/leanback-preference/src/main/java/androidx/leanback/preference/LeanbackListPreferenceDialogFragmentCompat.java
@@ -228,7 +228,8 @@
 
         @Override
         public void onBindViewHolder(ViewHolder holder, int position) {
-            holder.getWidgetView().setChecked(mEntryValues[position].equals(mSelectedValue));
+            holder.getWidgetView().setChecked(
+                    TextUtils.equals(mEntryValues[position].toString(), mSelectedValue));
             holder.getTitleView().setText(mEntries[position]);
         }
 
diff --git a/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java b/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
index d150b81..dbf5514 100644
--- a/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
+++ b/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
@@ -2208,6 +2208,11 @@
     @NonNull
     public ListenableFuture<PlayerResult> setAuxEffectSendLevel(
             @FloatRange(from = 0, to = 1) final float level) {
+        if (level < 0 || level > 1) {
+            // Returns ListenableFuture instead of throwing exception, not to newly throw an
+            // exception in existing code.
+            return createFutureForResultCode(RESULT_ERROR_BAD_VALUE);
+        }
         synchronized (mStateLock) {
             if (mClosed) {
                 return createFutureForClosed();
diff --git a/message-browser/browser/build.gradle b/message-browser/browser/build.gradle
index 03abbfe..dd0b94a4 100644
--- a/message-browser/browser/build.gradle
+++ b/message-browser/browser/build.gradle
@@ -15,6 +15,7 @@
 dependencies {
     implementation("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.0.0")
+    implementation("androidx.concurrent:concurrent-futures:1.0.0")
     api("androidx.annotation:annotation:1.1.0")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/message-browser/browser/src/androidTest/java/androidx/message/browser/MessageBrowserTest.java b/message-browser/browser/src/androidTest/java/androidx/message/browser/MessageBrowserTest.java
index 8d8763e..0281a36 100644
--- a/message-browser/browser/src/androidTest/java/androidx/message/browser/MessageBrowserTest.java
+++ b/message-browser/browser/src/androidTest/java/androidx/message/browser/MessageBrowserTest.java
@@ -16,6 +16,7 @@
 
 package androidx.message.browser;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -26,6 +27,8 @@
 import androidx.annotation.NonNull;
 import androidx.test.core.app.ApplicationProvider;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -126,6 +129,59 @@
         assertTrue(callback.disconnectedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
     }
 
+    @Test
+    public void testSendCustomCommand() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        MessageBrowser.BrowserCallback callback = new MessageBrowser.BrowserCallback() {
+            public void onConnected(@NonNull MessageBrowser browser,
+                    @NonNull MessageCommandGroup allowedCommands) {
+                latch.countDown();
+            }
+        };
+        mMsgBrowserBuilder.setBrowserCallback(sHandlerExecutor, callback);
+        MessageBrowser browser = mMsgBrowserBuilder.build();
+        Bundle cmdExtras = new Bundle();
+        cmdExtras.putString("cmd-extra", "cmd-extra-value");
+        Bundle cmdArgs = new Bundle();
+        cmdArgs.putString("cmd-args", "cmd-args-value");
+        MessageCommand command =
+                new MessageCommand(MockMessageLibraryService.CUSTOM_COMMAND_ACCEPT, cmdExtras);
+        ListenableFuture<Bundle> resultFuture = browser.sendCustomCommand(command, cmdArgs);
+        Bundle result = resultFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        assertEquals(MockMessageLibraryService.CUSTOM_COMMAND_ACCEPT,
+                result.getString(MockMessageLibraryService.KEY_CUSTOM_COMMAND_ACTION));
+        assertEquals("cmd-extra-value", result
+                .getBundle(MockMessageLibraryService.KEY_CUSTOM_COMMAND_EXTRAS)
+                .getString("cmd-extra"));
+        assertEquals("cmd-args-value", result
+                .getBundle(MockMessageLibraryService.KEY_CUSTOM_COMMAND_ARGS)
+                .getString("cmd-args"));
+        browser.close();
+    }
+
+    @Test
+    public void testSendCustomCommandDecline() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        MessageBrowser.BrowserCallback callback = new MessageBrowser.BrowserCallback() {
+            public void onConnected(@NonNull MessageBrowser browser,
+                    @NonNull MessageCommandGroup allowedCommands) {
+                latch.countDown();
+            }
+        };
+        mMsgBrowserBuilder.setBrowserCallback(sHandlerExecutor, callback);
+        MessageBrowser browser = mMsgBrowserBuilder.build();
+        Bundle cmdExtras = new Bundle();
+        cmdExtras.putString("cmd-extra", "cmd-extra-value");
+        Bundle cmdArgs = new Bundle();
+        cmdArgs.putString("cmd-args", "cmd-args-value");
+        MessageCommand command =
+                new MessageCommand(MockMessageLibraryService.CUSTOM_COMMAND_DECLINE, cmdExtras);
+        ListenableFuture<Bundle> resultFuture = browser.sendCustomCommand(command, cmdArgs);
+        Bundle result = resultFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        assertTrue(result.isEmpty());
+        browser.close();
+    }
+
     private class BrowserCallbackForTestMultiConnection extends MessageBrowser.BrowserCallback {
         public MessageBrowser browser1;
         public final CountDownLatch connectedLatch1 = new CountDownLatch(1);
diff --git a/message-browser/browser/src/androidTest/java/androidx/message/browser/MockMessageLibraryService.java b/message-browser/browser/src/androidTest/java/androidx/message/browser/MockMessageLibraryService.java
index ee061d1..ebf8ba6 100644
--- a/message-browser/browser/src/androidTest/java/androidx/message/browser/MockMessageLibraryService.java
+++ b/message-browser/browser/src/androidTest/java/androidx/message/browser/MockMessageLibraryService.java
@@ -18,6 +18,9 @@
 
 import android.os.Bundle;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 /**
  * Mock implementation of {@link MessageLibraryService} for testing.
  */
@@ -28,6 +31,16 @@
             "MockMessageLibraryService.KEY_REFUSE_CONNECTION";
     public static final String KEY_CRASH_CONNECTION =
             "MockMessageLibraryService.KEY_CRASH_CONNECTION";
+    public static final String CUSTOM_COMMAND_ACCEPT =
+            "MockMessageLibraryService.CUSTOM_COMMAND_ACCEPT";
+    public static final String CUSTOM_COMMAND_DECLINE =
+            "MockMessageLibraryService.CUSTOM_COMMAND_DECLINE";
+    public static final String KEY_CUSTOM_COMMAND_ACTION =
+            "MockMessageLibraryService.CUSTOM_COMMAND_ACTION";
+    public static final String KEY_CUSTOM_COMMAND_ARGS =
+            "MockMessageLibraryService.CUSTOM_COMMAND_ARGS";
+    public static final String KEY_CUSTOM_COMMAND_EXTRAS =
+            "MockMessageLibraryService.CUSTOM_COMMAND_EXTRAS";
 
     public MockMessageLibraryService() {
         super();
@@ -39,7 +52,7 @@
     }
 
     @Override
-    public MessageCommandGroup onConnect(BrowserInfo info) {
+    public MessageCommandGroup onConnect(@NonNull BrowserInfo info) {
         Bundle connectionHints = info.getConnectionHints();
         if (connectionHints.getBoolean(KEY_REFUSE_CONNECTION)) {
             return null;
@@ -48,4 +61,24 @@
         }
         return super.onConnect(info);
     }
+
+    @Override
+    public boolean onCommandRequest(@NonNull BrowserInfo browserInfo,
+            @NonNull MessageCommand command) {
+        if (command.mCommandCode == MessageCommand.COMMAND_CODE_CUSTOM) {
+            return CUSTOM_COMMAND_ACCEPT.equals(command.mCustomAction);
+        }
+        return true;
+    }
+
+    @Override
+    @NonNull
+    public Bundle onCustomCommand(@NonNull BrowserInfo browserInfo,
+            @NonNull MessageCommand customCommand, @Nullable Bundle args) {
+        Bundle result = new Bundle();
+        result.putString(KEY_CUSTOM_COMMAND_ACTION, customCommand.mCustomAction);
+        result.putBundle(KEY_CUSTOM_COMMAND_EXTRAS, customCommand.mCustomExtras);
+        result.putBundle(KEY_CUSTOM_COMMAND_ARGS, args);
+        return result;
+    }
 }
diff --git a/message-browser/browser/src/main/aidl/androidx/message/browser/IMessageBrowser.aidl b/message-browser/browser/src/main/aidl/androidx/message/browser/IMessageBrowser.aidl
index 1c03aa2..cdf6823 100644
--- a/message-browser/browser/src/main/aidl/androidx/message/browser/IMessageBrowser.aidl
+++ b/message-browser/browser/src/main/aidl/androidx/message/browser/IMessageBrowser.aidl
@@ -25,6 +25,7 @@
  * @hide
  */
 oneway interface IMessageBrowser {
-    void notifyConnected(in Bundle allowedCommands) = 0;
-    void notifyDisconnected() = 1;
+    void notifyConnected(int seq, in Bundle allowedCommands) = 0;
+    void notifyDisconnected(int seq) = 1;
+    void notifyCommandResult(int seq, in Bundle result) = 2;
 }
diff --git a/message-browser/browser/src/main/aidl/androidx/message/browser/IMessageLibraryService.aidl b/message-browser/browser/src/main/aidl/androidx/message/browser/IMessageLibraryService.aidl
index 81fa472..227ec23 100644
--- a/message-browser/browser/src/main/aidl/androidx/message/browser/IMessageLibraryService.aidl
+++ b/message-browser/browser/src/main/aidl/androidx/message/browser/IMessageLibraryService.aidl
@@ -29,4 +29,5 @@
 oneway interface IMessageLibraryService {
     void connect(IMessageBrowser browser, int seq, in Bundle connectionRequest) = 0;
     void disconnect(IMessageBrowser browser, int seq) = 1;
+    void sendCustomCommand(IMessageBrowser browser, int seq, in Bundle command, in Bundle args) = 2;
 }
diff --git a/message-browser/browser/src/main/java/androidx/message/browser/MessageBrowser.java b/message-browser/browser/src/main/java/androidx/message/browser/MessageBrowser.java
index 0040604..2bc542f 100644
--- a/message-browser/browser/src/main/java/androidx/message/browser/MessageBrowser.java
+++ b/message-browser/browser/src/main/java/androidx/message/browser/MessageBrowser.java
@@ -33,6 +33,10 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
@@ -73,6 +77,7 @@
 
     final BrowserCallback mBrowserCallback;
     final Executor mCallbackExecutor;
+    final SequencedFutureManager mSequencedFutureManager;
 
     private final MessageLibraryServiceConnection mServiceConnection;
 
@@ -84,7 +89,8 @@
     final ConnectionRequest mConnectionRequest;
     @GuardedBy("mLock")
     @SuppressWarnings("WeakerAccess") /* synthetic access */
-    int mNextSequenceNumber;
+    final List<Runnable> mPendingTasks = new ArrayList<>();
+
     @GuardedBy("mLock")
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     IMessageLibraryService mService;
@@ -105,6 +111,7 @@
         mServiceComponent = serviceComponent;
         mCallbackExecutor = callbackExecutor;
         mBrowserCallback = callback;
+        mSequencedFutureManager = new SequencedFutureManager();
         mConnectionRequest = new ConnectionRequest(mContext.getPackageName(), Process.myPid(),
                 connectionHints);
         mBrowserStub = new BrowserStub();
@@ -124,29 +131,67 @@
             if (mBrowserState == STATE_CLOSED || mBrowserState == STATE_CLOSING) return;
             try {
                 if (mBrowserState == STATE_CONNECTING || mBrowserState == STATE_CONNECTED) {
-                    mService.disconnect(mBrowserStub, mNextSequenceNumber++);
+                    mService.disconnect(mBrowserStub,
+                            mSequencedFutureManager.obtainNextSequenceNumber());
                 }
             } catch (RemoteException e) {
                 Log.w(TAG, "Service " + mServiceComponent + " has died prematurely");
             }
             mBrowserState = STATE_CLOSING;
+            mSequencedFutureManager.close();
         }
     }
 
-    void notifyConnected(MessageCommandGroup allowedCommands) {
-        if (mCallbackExecutor != null) {
-            mCallbackExecutor.execute(() -> {
-                mBrowserCallback.onConnected(MessageBrowser.this, allowedCommands);
-            });
+    /**
+     * Sends a custom command to the library service
+     * <p>
+     * A command is not accepted if it is not a custom command.
+     *
+     * @param command custom command to be sent to the library service
+     * @param args optional argument
+     *
+     * @return a listenable future that contains a {@link Bundle} for the result.
+     */
+    @NonNull
+    public ListenableFuture<Bundle> sendCustomCommand(@NonNull MessageCommand command,
+            @Nullable Bundle args) {
+        if (command == null) {
+            throw new NullPointerException("command shouldn't be null");
         }
+        if (command.getCommandCode() != MessageCommand.COMMAND_CODE_CUSTOM) {
+            throw new IllegalArgumentException("command should be a custom command");
+        }
+        final SequencedFutureManager.SequencedFuture<Bundle> result =
+                mSequencedFutureManager.createSequencedFuture(Bundle.EMPTY);
+        executeOrPendRunnable(() -> {
+            synchronized (mLock) {
+                try {
+                    mService.sendCustomCommand(mBrowserStub, result.getSequenceNumber(),
+                            command.toBundle(), args);
+                } catch (RemoteException e) {
+                    mSequencedFutureManager.setFutureResult(result.getSequenceNumber(),
+                            Bundle.EMPTY);
+                }
+            }
+        });
+        return result;
     }
 
-    void notifyDisconnected() {
-        if (mCallbackExecutor != null) {
-            mCallbackExecutor.execute(() -> {
-                mBrowserCallback.onDisconnected(MessageBrowser.this);
-            });
+    void executeOrPendRunnable(Runnable runnable) {
+        synchronized (mLock) {
+            if (mBrowserState != STATE_CONNECTED) {
+                mPendingTasks.add(runnable);
+                return;
+            }
         }
+        runnable.run();
+    }
+
+    void notifyBrowserCallback(Runnable runnable) {
+        if (mCallbackExecutor != null) {
+            mCallbackExecutor.execute(() -> runnable.run());
+        }
+
     }
 
     private boolean requestConnection() {
@@ -273,23 +318,37 @@
         BrowserStub() {}
 
         @Override
-        public void notifyConnected(final Bundle allowedCommands) {
+        public void notifyConnected(final int seq, final Bundle allowedCommands) {
             synchronized (mLock) {
                 mBrowserState = STATE_CONNECTED;
             }
-            MessageBrowser.this.notifyConnected(MessageCommandGroup.fromBundle(allowedCommands));
+            notifyBrowserCallback(() -> mBrowserCallback.onConnected(MessageBrowser.this,
+                    MessageCommandGroup.fromBundle(allowedCommands)));
+            synchronized (mLock) {
+                for (Runnable task : mPendingTasks) {
+                    task.run();
+                }
+                mPendingTasks.clear();
+            }
         }
 
         @Override
-        public void notifyDisconnected() {
+        public void notifyDisconnected(final int seq) {
             synchronized (mLock) {
                 if (mBrowserState == STATE_CLOSING) {
                     mBrowserState = STATE_CLOSED;
                 } else if (mBrowserState != STATE_ERROR) {
                     mBrowserState = STATE_DISCONNECTED;
                 }
+                mPendingTasks.clear();
             }
-            MessageBrowser.this.notifyDisconnected();
+            mSequencedFutureManager.close();
+            notifyBrowserCallback(() -> mBrowserCallback.onDisconnected(MessageBrowser.this));
+        }
+
+        @Override
+        public void notifyCommandResult(final int seq, final Bundle result) {
+            mSequencedFutureManager.setFutureResult(seq, result);
         }
     }
 
@@ -300,6 +359,7 @@
         @Override
         public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
             // Note that it's always main-thread.
+            int browserState = STATE_IDLE;
             if (DEBUG) {
                 Log.d(TAG, "onServiceConnected " + componentName + " " + this);
             }
@@ -308,26 +368,28 @@
                 if (!mServiceComponent.equals(componentName)) {
                     Log.wtf(TAG, "Expected connection to " + mServiceComponent + " but is"
                             + " connected to " + componentName);
-                    mBrowserState = STATE_ERROR;
-                    return;
+                    browserState = mBrowserState = STATE_ERROR;
+                } else {
+                    mService = IMessageLibraryService.Stub.asInterface(iBinder);
+                    if (mService == null) {
+                        Log.wtf(TAG, "Service interface is missing.");
+                        browserState = mBrowserState = STATE_ERROR;
+                    } else {
+                        browserState = mBrowserState = STATE_CONNECTING;
+                        try {
+                            mService.connect(mBrowserStub,
+                                    mSequencedFutureManager.obtainNextSequenceNumber(),
+                                    mConnectionRequest.toBundle());
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "Service " + mServiceComponent + " has died prematurely");
+                            browserState = mBrowserState = STATE_ERROR;
+                        }
+                    }
                 }
-                mService = IMessageLibraryService.Stub.asInterface(iBinder);
-                if (mService == null) {
-                    Log.wtf(TAG, "Service interface is missing.");
-                    mBrowserState = STATE_ERROR;
-                    MessageBrowser.this.notifyDisconnected();
-                    return;
-                }
-                mBrowserState = STATE_CONNECTING;
-                try {
-                    mService.connect(mBrowserStub, mNextSequenceNumber++,
-                            mConnectionRequest.toBundle());
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Service " + mServiceComponent + " has died prematurely");
-                    mBrowserState = STATE_ERROR;
-                    MessageBrowser.this.notifyDisconnected();
-                    close();
-                }
+            }
+            if (browserState == STATE_ERROR) {
+                notifyBrowserCallback(
+                        () -> mBrowserCallback.onDisconnected(MessageBrowser.this));
             }
         }
 
diff --git a/message-browser/browser/src/main/java/androidx/message/browser/MessageLibraryService.java b/message-browser/browser/src/main/java/androidx/message/browser/MessageLibraryService.java
index cadfe16..7933bcc 100644
--- a/message-browser/browser/src/main/java/androidx/message/browser/MessageLibraryService.java
+++ b/message-browser/browser/src/main/java/androidx/message/browser/MessageLibraryService.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -66,11 +67,14 @@
     final ArrayMap<IBinder, BrowserRecord> mBrowserRecords = new ArrayMap<>();
 
     private ServiceStub mServiceStub;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    Handler mHandler;
 
     @Override
     public void onCreate() {
         super.onCreate();
         mServiceStub = new ServiceStub();
+        mHandler = new Handler();
     }
 
     @Nullable
@@ -108,6 +112,46 @@
     }
 
     /**
+     * Called when a browser sent a command which will be sent directly.
+     * <p>
+     * Return {@code true} to accept the command, {@code false} to decline the command.
+     *
+     * @param browserInfo the browser information
+     * @param command a command. This method will be called for every single command.
+     * @return {@code RESULT_SUCCESS} if you want to proceed with incoming command.
+     *         Another code for ignore.
+     */
+    public boolean onCommandRequest(@NonNull BrowserInfo browserInfo,
+            @NonNull MessageCommand command) {
+        return true;
+    }
+
+    /**
+     * Called when a browser sent a custom command through
+     * {@link MessageBrowser#sendCustomCommand(MessageCommand, Bundle)}.
+     * <p>
+     * @param browserInfo the browser information
+     * @param customCommand custom command.
+     * @param args optional arguments
+     * @return result of handling custom command. A runtime exception will be thrown if
+     *         {@code null} is returned.
+     * @see MessageCommand#COMMAND_CODE_CUSTOM
+     */
+    @NonNull
+    public Bundle onCustomCommand(@NonNull BrowserInfo browserInfo,
+            @NonNull MessageCommand customCommand, @Nullable Bundle args) {
+        return Bundle.EMPTY;
+    }
+
+    void releaseBrowserRecord(BrowserRecord record) {
+        IBinder browserBinder = record.browser.asBinder();
+        browserBinder.unlinkToDeath(record, 0);
+        synchronized (mLock) {
+            mBrowserRecords.remove(browserBinder);
+        }
+    }
+
+    /**
      * Information of the connected browser.
      * @hide
      */
@@ -167,7 +211,7 @@
             final ConnectionRequest request = ConnectionRequest.fromBundle(connectionRequest);
             if (request == null) {
                 try {
-                    browser.notifyDisconnected();
+                    browser.notifyDisconnected(seq);
                 } catch (RemoteException ex) {
                     Log.w(TAG, "Calling notifyDisconnected() failed");
                 }
@@ -194,7 +238,7 @@
                     }
                     try {
                         browserBinder.linkToDeath(record, 0);
-                        browser.notifyConnected(allowedCommands.toBundle());
+                        browser.notifyConnected(seq, allowedCommands.toBundle());
                     } catch (RemoteException ex) {
                         Log.w(TAG, "Calling notifyConnected() failed");
                         synchronized (mLock) {
@@ -203,7 +247,7 @@
                     }
                 } else {
                     try {
-                        browser.notifyDisconnected();
+                        browser.notifyDisconnected(seq);
                     } catch (RemoteException ex) {
                         Log.w(TAG, "Calling notifyDisconnected() failed");
                     }
@@ -220,12 +264,34 @@
                 BrowserRecord record = mBrowserRecords.remove(browserBinder);
                 try {
                     browserBinder.unlinkToDeath(record, 0);
-                    browser.notifyDisconnected();
+                    browser.notifyDisconnected(seq);
                 } catch (RemoteException e) {
                     Log.w(TAG, "Calling notifyDisconnected() failed");
                 }
             }
         }
+
+        @Override
+        public void sendCustomCommand(IMessageBrowser browser, int seq, Bundle command,
+                Bundle args) {
+            synchronized (mLock) {
+                BrowserRecord record = mBrowserRecords.get(browser.asBinder());
+                if (record == null) {
+                    return;
+                }
+                MessageCommand customCommand = MessageCommand.fromBundle(command);
+                mHandler.post(() -> {
+                    try {
+                        Bundle result = onCommandRequest(record.browserInfo, customCommand)
+                                ? onCustomCommand(record.browserInfo, customCommand, args)
+                                : Bundle.EMPTY;
+                        record.browser.notifyCommandResult(seq, result);
+                    } catch (RemoteException e) {
+                        releaseBrowserRecord(record);
+                    }
+                });
+            }
+        }
     }
 
     private class BrowserRecord implements IBinder.DeathRecipient {
diff --git a/message-browser/browser/src/main/java/androidx/message/browser/SequencedFutureManager.java b/message-browser/browser/src/main/java/androidx/message/browser/SequencedFutureManager.java
new file mode 100644
index 0000000..038621341
--- /dev/null
+++ b/message-browser/browser/src/main/java/androidx/message/browser/SequencedFutureManager.java
@@ -0,0 +1,137 @@
+/*
+ * 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.message.browser;
+
+import android.annotation.SuppressLint;
+import android.util.Log;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.collection.ArrayMap;
+import androidx.concurrent.futures.AbstractResolvableFuture;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages {@link SequencedFuture} that contains sequence number to be shared across the process.
+ */
+class SequencedFutureManager {
+    private static final String TAG = "SequencedFutureManager";
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    final Object mLock = new Object();
+    @GuardedBy("mLock")
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    final ArrayMap<Integer, SequencedFuture<?>> mSeqToFutureMap = new ArrayMap<>();
+    @GuardedBy("mLock")
+    private int mNextSequenceNumber;
+
+    /**
+     * Obtains next sequence number without creating future. Used for methods with no return
+     * (e.g. close())
+     *
+     * @return sequence number
+     */
+    public int obtainNextSequenceNumber() {
+        synchronized (mLock) {
+            return mNextSequenceNumber++;
+        }
+    }
+
+    /**
+     * Creates {@link SequencedFuture} with sequence number. Used to return
+     * {@link ListenableFuture} for remote process call.
+     *
+     * @return AbstractFuture with sequence number
+     */
+    public <T> SequencedFuture<T> createSequencedFuture(T resultWhenClosed) {
+        synchronized (mLock) {
+            int seq = obtainNextSequenceNumber();
+            SequencedFuture<T> result = new SequencedFuture<>(seq, resultWhenClosed);
+            mSeqToFutureMap.put(seq, result);
+            return result;
+        }
+    }
+
+    /**
+     * Sets result of the {@link SequencedFuture} with the sequence id. Specified future will be
+     * removed from the manager.
+     *
+     * @param seq sequence number to find future
+     * @param result result to set
+     */
+    @SuppressWarnings("unchecked")
+    public <T> void setFutureResult(int seq, T result) {
+        synchronized (mLock) {
+            SequencedFuture<?> future = mSeqToFutureMap.remove(seq);
+            if (future != null) {
+                if (result == null
+                        || future.getResultWhenClosed().getClass() == result.getClass()) {
+                    ((SequencedFuture<T>) future).set(result);
+                } else {
+                    Log.w(TAG, "Type mismatch, expected "
+                            + future.getResultWhenClosed().getClass()
+                            + ", but was " + result.getClass());
+                }
+            }
+        }
+    }
+
+    public void close() {
+        List<SequencedFuture<?>> pendingResults;
+        synchronized (mLock) {
+            pendingResults = new ArrayList<>(mSeqToFutureMap.values());
+            mSeqToFutureMap.clear();
+        }
+        for (SequencedFuture<?> result: pendingResults) {
+            result.setWithTheValueOfResultWhenClosed();
+        }
+    }
+
+    // TODO: Find a way to remove @SuppressLint
+    @SuppressLint("RestrictedApi")
+    static final class SequencedFuture<T> extends AbstractResolvableFuture<T> {
+        private final int mSequenceNumber;
+        private final T mResultWhenClosed;
+
+        SequencedFuture(int seq, @NonNull T resultWhenClosed) {
+            mSequenceNumber = seq;
+            mResultWhenClosed = resultWhenClosed;
+        }
+
+        @Override
+        public boolean set(@Nullable T value) {
+            return super.set(value);
+        }
+
+        void setWithTheValueOfResultWhenClosed() {
+            set(mResultWhenClosed);
+        }
+
+        public int getSequenceNumber() {
+            return mSequenceNumber;
+        }
+
+        public @NonNull T getResultWhenClosed() {
+            return mResultWhenClosed;
+        }
+    }
+}
diff --git a/navigation/navigation-dynamic-features-core/src/main/java/androidx/navigation/dynamicfeatures/DynamicInstallManager.kt b/navigation/navigation-dynamic-features-core/src/main/java/androidx/navigation/dynamicfeatures/DynamicInstallManager.kt
index 6278207..afee9f4 100644
--- a/navigation/navigation-dynamic-features-core/src/main/java/androidx/navigation/dynamicfeatures/DynamicInstallManager.kt
+++ b/navigation/navigation-dynamic-features-core/src/main/java/androidx/navigation/dynamicfeatures/DynamicInstallManager.kt
@@ -27,6 +27,7 @@
 import androidx.navigation.get
 import com.google.android.play.core.splitcompat.SplitCompat
 import com.google.android.play.core.splitinstall.SplitInstallException
+import com.google.android.play.core.splitinstall.SplitInstallHelper
 import com.google.android.play.core.splitinstall.SplitInstallManager
 import com.google.android.play.core.splitinstall.SplitInstallRequest
 import com.google.android.play.core.splitinstall.SplitInstallSessionState
@@ -37,7 +38,7 @@
 /**
  * Install manager for dynamic features.
  *
- * Enables installation of dynamic features.
+ * Enables installation of dynamic features for both installed and instant apps.
  */
 open class DynamicInstallManager(
     private val context: Context,
@@ -172,6 +173,8 @@
             if (splitInstallSessionState.sessionId() == installMonitor.sessionId) {
                 if (splitInstallSessionState.status() == SplitInstallSessionStatus.INSTALLED) {
                     SplitCompat.install(context)
+                    // Enable immediate usage of dynamic feature modules in an instant app context.
+                    SplitInstallHelper.updateAppInfo(context)
                 }
                 status.value = splitInstallSessionState
                 if (splitInstallSessionState.hasTerminalStatus()) {
diff --git a/navigation/navigation-dynamic-features-fragment/src/main/res/values-ar/strings.xml b/navigation/navigation-dynamic-features-fragment/src/main/res/values-ar/strings.xml
index cb7c90e..fb5161a 100644
--- a/navigation/navigation-dynamic-features-fragment/src/main/res/values-ar/strings.xml
+++ b/navigation/navigation-dynamic-features-fragment/src/main/res/values-ar/strings.xml
@@ -21,8 +21,6 @@
     <string name="installation_cancelled" msgid="475402237100444685">"تم إلغاء التثبيت."</string>
     <string name="installing_module" msgid="5968445461040787716">"جارٍ تثبيت الوحدة:"</string>
     <string name="progress" msgid="8366783942222789124">"التقدم:"</string>
-    <!-- no translation found for retry (1065327189183624288) -->
-    <skip />
-    <!-- no translation found for ok (4702104660890557116) -->
-    <skip />
+    <string name="retry" msgid="1065327189183624288">"إعادة المحاولة"</string>
+    <string name="ok" msgid="4702104660890557116">"حسنًا"</string>
 </resources>
diff --git a/navigation/navigation-dynamic-features-fragment/src/main/res/values-eu/strings.xml b/navigation/navigation-dynamic-features-fragment/src/main/res/values-eu/strings.xml
index d570108..01caed0 100644
--- a/navigation/navigation-dynamic-features-fragment/src/main/res/values-eu/strings.xml
+++ b/navigation/navigation-dynamic-features-fragment/src/main/res/values-eu/strings.xml
@@ -21,8 +21,6 @@
     <string name="installation_cancelled" msgid="475402237100444685">"Bertan behera utzi da instalazioa."</string>
     <string name="installing_module" msgid="5968445461040787716">"Modulua instalatzen:"</string>
     <string name="progress" msgid="8366783942222789124">"Garapena:"</string>
-    <!-- no translation found for retry (1065327189183624288) -->
-    <skip />
-    <!-- no translation found for ok (4702104660890557116) -->
-    <skip />
+    <string name="retry" msgid="1065327189183624288">"Saiatu berriro"</string>
+    <string name="ok" msgid="4702104660890557116">"Ados"</string>
 </resources>
diff --git a/navigation/navigation-dynamic-features-fragment/src/main/res/values-hi/strings.xml b/navigation/navigation-dynamic-features-fragment/src/main/res/values-hi/strings.xml
index c49ed96..cb05acf 100644
--- a/navigation/navigation-dynamic-features-fragment/src/main/res/values-hi/strings.xml
+++ b/navigation/navigation-dynamic-features-fragment/src/main/res/values-hi/strings.xml
@@ -21,8 +21,6 @@
     <string name="installation_cancelled" msgid="475402237100444685">"इंस्टॉल करना रद्द किया गया"</string>
     <string name="installing_module" msgid="5968445461040787716">"इंस्टॉल किया जा रहा मॉड्यूल:"</string>
     <string name="progress" msgid="8366783942222789124">"प्रगति:"</string>
-    <!-- no translation found for retry (1065327189183624288) -->
-    <skip />
-    <!-- no translation found for ok (4702104660890557116) -->
-    <skip />
+    <string name="retry" msgid="1065327189183624288">"फिर कोशिश करें"</string>
+    <string name="ok" msgid="4702104660890557116">"ठीक है"</string>
 </resources>
diff --git a/navigation/navigation-safe-args-gradle-plugin/build.gradle b/navigation/navigation-safe-args-gradle-plugin/build.gradle
index b5bc170..7d62068 100644
--- a/navigation/navigation-safe-args-gradle-plugin/build.gradle
+++ b/navigation/navigation-safe-args-gradle-plugin/build.gradle
@@ -35,6 +35,7 @@
     api(gradleApi())
     implementation(GSON)
     testImplementation(gradleTestKit())
+    testImplementation(project(":internal-testutils-gradle-plugin"))
     testImplementation(JUNIT)
 }
 
diff --git a/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/BasePluginTest.kt b/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/BasePluginTest.kt
index fc9ef2a..9b3c06c 100644
--- a/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/BasePluginTest.kt
+++ b/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/BasePluginTest.kt
@@ -16,17 +16,14 @@
 
 package androidx.navigation.safeargs.gradle
 
+import androidx.testutils.gradle.ProjectSetupRule
 import org.gradle.testkit.runner.BuildResult
 import org.gradle.testkit.runner.GradleRunner
 import org.gradle.testkit.runner.TaskOutcome
 import org.hamcrest.CoreMatchers
-import org.hamcrest.MatcherAssert
 import org.hamcrest.MatcherAssert.assertThat
-import org.junit.Before
 import org.junit.Rule
-import org.junit.rules.TemporaryFolder
 import java.io.File
-import java.util.Properties
 
 internal const val MAIN_DIR = "androidx/navigation/testapp"
 
@@ -46,20 +43,10 @@
 internal const val SEC = 1000L
 
 abstract class BasePluginTest {
-    @Suppress("MemberVisibilityCanBePrivate")
     @get:Rule
-    val testProjectDir = TemporaryFolder()
+    val projectSetup = ProjectSetupRule()
 
-    internal var buildFile: File = File("")
-    internal var prebuiltsRoot = ""
-    internal var compileSdkVersion = ""
-    internal var buildToolsVersion = ""
-    internal var minSdkVersion = ""
-    internal var debugKeystore = ""
-    internal var navigationCommon = ""
-    internal var kotlinStblib = ""
-
-    internal fun projectRoot(): File = testProjectDir.root
+    internal fun projectRoot(): File = projectSetup.rootDir
 
     internal fun assertGenerated(name: String, prefix: String? = null): File {
         return prefix?.let { assertExists(name, true, it) } ?: assertExists(name, true)
@@ -74,7 +61,7 @@
             projectRoot(),
             "${prefix}build/$GENERATED_PATH/$name"
         )
-        MatcherAssert.assertThat(
+        assertThat(
             generatedFile.exists(),
             CoreMatchers.`is`(ex)
         )
@@ -90,92 +77,40 @@
     internal fun runGradle(vararg args: String) = gradleBuilder(*args).build()
     internal fun runAndFailGradle(vararg args: String) = gradleBuilder(*args).buildAndFail()
 
-    @Before
-    fun setup() {
-        projectRoot().mkdirs()
-        buildFile = File(projectRoot(), "build.gradle")
-        buildFile.createNewFile()
-        // copy local.properties
-        val appToolkitProperties = File("../../local.properties")
-        if (appToolkitProperties.exists()) {
-            appToolkitProperties.copyTo(
-                File(projectRoot(), "local.properties"),
-                overwrite = true
-            )
-        } else {
-            File("../../local.properties").copyTo(
-                File(projectRoot(), "local.properties"), overwrite = true
-            )
-        }
-        val stream = BasePluginTest::class.java.classLoader.getResourceAsStream("sdk.prop")
-        val properties = Properties()
-        properties.load(stream)
-        prebuiltsRoot = properties.getProperty("prebuiltsRoot")
-        compileSdkVersion = properties.getProperty("compileSdkVersion")
-        buildToolsVersion = properties.getProperty("buildToolsVersion")
-        minSdkVersion = properties.getProperty("minSdkVersion")
-        debugKeystore = properties.getProperty("debugKeystore")
-        navigationCommon = properties.getProperty("navigationCommon")
-        kotlinStblib = properties.getProperty("kotlinStdlib")
-
-        val propertiesFile = File(projectRoot(), "gradle.properties")
-        propertiesFile.writer().use {
-            val props = Properties()
-            props.setProperty("android.useAndroidX", "true")
-            props.store(it, null)
-        }
-    }
-
     internal fun setupSimpleBuildGradle() {
         testData("app-project").copyRecursively(projectRoot())
-        buildFile.writeText("""
-            plugins {
-                id('com.android.application')
-                id('androidx.navigation.safeargs')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
+        projectSetup.writeDefaultBuildGradle(
+            prefix = """
+                plugins {
+                    id('com.android.application')
+                    id('androidx.navigation.safeargs')
                 }
-
-                signingConfigs {
-                    debug {
-                        storeFile file("$debugKeystore")
-                    }
+            """.trimIndent(),
+            suffix = """
+                dependencies {
+                    implementation "${projectSetup.props.navigationCommon}"
                 }
-            }
-
-            dependencies {
-                implementation "$navigationCommon"
-            }
-        """.trimIndent()
+            """.trimIndent()
         )
     }
 
     internal fun setupMultiModuleBuildGradle() {
         testData("multimodule-project").copyRecursively(projectRoot())
-        buildFile.writeText("""
+        val props = projectSetup.props
+        projectSetup.buildFile.writeText(
+            """
             buildscript {
-                ext.compileSdk = $compileSdkVersion
-                ext.buildTools = "$buildToolsVersion"
-                ext.minSdk = $minSdkVersion
-                ext.debugKeystoreFile = "$debugKeystore"
-                ext.navigationCommonDep = "$navigationCommon"
+                ext.compileSdk = ${props.compileSdkVersion}
+                ext.buildTools = "${props.buildToolsVersion}"
+                ext.minSdk = ${props.minSdkVersion}
+                ext.debugKeystoreFile = "${props.debugKeystore}"
+                ext.navigationCommonDep = "${props.navigationCommon}"
             }
 
             allprojects {
                 repositories {
-                    maven { url "$prebuiltsRoot/androidx/external" }
-                    maven { url "$prebuiltsRoot/androidx/internal" }
+                    maven { url "${props.prebuiltsRoot}/androidx/external" }
+                    maven { url "${props.prebuiltsRoot}/androidx/internal" }
                 }
             }
         """.trimIndent()
@@ -184,38 +119,20 @@
 
     internal fun setupSimpleKotlinBuildGradle() {
         testData("app-project-kotlin").copyRecursively(projectRoot())
-        buildFile.writeText("""
-            plugins {
-                id('com.android.application')
-                id('kotlin-android')
-                id('androidx.navigation.safeargs.kotlin')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
+        projectSetup.writeDefaultBuildGradle(
+            prefix = """
+                plugins {
+                    id('com.android.application')
+                    id('kotlin-android')
+                    id('androidx.navigation.safeargs.kotlin')
                 }
-
-                signingConfigs {
-                    debug {
-                        storeFile file("$debugKeystore")
-                    }
+            """.trimIndent(),
+            suffix = """
+                dependencies {
+                    implementation "${projectSetup.props.kotlinStblib}"
+                    implementation "${projectSetup.props.navigationCommon}"
                 }
-            }
-
-            dependencies {
-                implementation "$kotlinStblib"
-                implementation "$navigationCommon"
-            }
-        """.trimIndent()
+            """.trimIndent()
         )
     }
 }
diff --git a/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/JavaPluginTest.kt b/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/JavaPluginTest.kt
index 114186e..9398dcdb4 100644
--- a/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/JavaPluginTest.kt
+++ b/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/JavaPluginTest.kt
@@ -27,49 +27,32 @@
     @Test
     fun runGenerateTask() {
         testData("app-project").copyRecursively(projectRoot())
-        buildFile.writeText("""
-            plugins {
-                id('com.android.application')
-                id('androidx.navigation.safeargs')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-                flavorDimensions "mode"
-                productFlavors {
-                    foo {
-                        dimension "mode"
-                        applicationIdSuffix ".foo"
-                    }
-                    notfoo {
-                        dimension "mode"
-                    }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = """
+                plugins {
+                    id('com.android.application')
+                    id('androidx.navigation.safeargs')
                 }
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
-                }
-
-                signingConfigs {
-                    debug {
-                        storeFile file("$debugKeystore")
+            """.trimIndent(),
+            suffix = """
+                android {
+                    flavorDimensions "mode"
+                    productFlavors {
+                        foo {
+                            dimension "mode"
+                            applicationIdSuffix ".foo"
+                        }
+                        notfoo {
+                            dimension "mode"
+                        }
                     }
                 }
-            }
 
-            dependencies {
-                implementation "$navigationCommon"
-            }
-        """.trimIndent()
+                dependencies {
+                    implementation "${projectSetup.props.navigationCommon}"
+                }
+            """.trimIndent()
         )
-
         runGradle("assembleNotfooDebug", "assembleFooDebug")
             .assertSuccessfulTask("assembleNotfooDebug")
             .assertSuccessfulTask("assembleFooDebug")
diff --git a/paging/common/api/3.0.0-alpha01.txt b/paging/common/api/3.0.0-alpha01.txt
index 4ef6eae..9dab050 100644
--- a/paging/common/api/3.0.0-alpha01.txt
+++ b/paging/common/api/3.0.0-alpha01.txt
@@ -143,8 +143,8 @@
   }
 
   public final class PagedDataFlowBuilder<Key, Value> {
-    ctor public PagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
-    ctor @Deprecated public PagedDataFlowBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public PagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagingConfig config);
+    ctor @Deprecated public PagedDataFlowBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagingConfig config);
     method public kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<Value>> build();
     method public androidx.paging.PagedDataFlowBuilder<Key,Value> setInitialKey(Key? initialKey);
   }
@@ -292,6 +292,29 @@
   public final class PagedSourceKt {
   }
 
+  public final class PagingConfig {
+    ctor public PagingConfig(int pageSize, int prefetchDistance, boolean enablePlaceholders, int initialLoadSize, int maxSize);
+    field public static final androidx.paging.PagingConfig.Companion! Companion;
+    field public static final int MAX_SIZE_UNBOUNDED = 2147483647; // 0x7fffffff
+    field public final boolean enablePlaceholders;
+    field public final int initialLoadSize;
+    field public final int maxSize;
+    field public final int pageSize;
+    field public final int prefetchDistance;
+  }
+
+  public static final class PagingConfig.Builder {
+    ctor public PagingConfig.Builder(int pageSize);
+    method public androidx.paging.PagingConfig build();
+    method public androidx.paging.PagingConfig.Builder setEnablePlaceholders(boolean enablePlaceholders);
+    method public androidx.paging.PagingConfig.Builder setInitialLoadSizeHint(@IntRange(from=1) int initialLoadSizeHint);
+    method public androidx.paging.PagingConfig.Builder setMaxSize(@IntRange(from=2) int maxSize);
+    method public androidx.paging.PagingConfig.Builder setPrefetchDistance(@IntRange(from=0) int prefetchDistance);
+  }
+
+  public static final class PagingConfig.Companion {
+  }
+
   public abstract class PositionalDataSource<T> extends androidx.paging.DataSource<java.lang.Integer,T> {
     ctor public PositionalDataSource();
     method public static final int computeInitialLoadPosition(androidx.paging.PositionalDataSource.LoadInitialParams params, int totalCount);
diff --git a/paging/common/api/api_lint.ignore b/paging/common/api/api_lint.ignore
index 7a58a78..e6f73d2 100644
--- a/paging/common/api/api_lint.ignore
+++ b/paging/common/api/api_lint.ignore
@@ -9,6 +9,8 @@
 
 MinMaxConstant: androidx.paging.PagedList.Config#MAX_SIZE_UNBOUNDED:
     If min/max could change in future, make them dynamic methods: androidx.paging.PagedList.Config#MAX_SIZE_UNBOUNDED
+MinMaxConstant: androidx.paging.PagingConfig#MAX_SIZE_UNBOUNDED:
+    If min/max could change in future, make them dynamic methods: androidx.paging.PagingConfig#MAX_SIZE_UNBOUNDED
 
 
 RegistrationName: androidx.paging.DataSource#addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback):
diff --git a/paging/common/api/current.txt b/paging/common/api/current.txt
index 4ef6eae..9dab050 100644
--- a/paging/common/api/current.txt
+++ b/paging/common/api/current.txt
@@ -143,8 +143,8 @@
   }
 
   public final class PagedDataFlowBuilder<Key, Value> {
-    ctor public PagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
-    ctor @Deprecated public PagedDataFlowBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public PagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagingConfig config);
+    ctor @Deprecated public PagedDataFlowBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagingConfig config);
     method public kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<Value>> build();
     method public androidx.paging.PagedDataFlowBuilder<Key,Value> setInitialKey(Key? initialKey);
   }
@@ -292,6 +292,29 @@
   public final class PagedSourceKt {
   }
 
+  public final class PagingConfig {
+    ctor public PagingConfig(int pageSize, int prefetchDistance, boolean enablePlaceholders, int initialLoadSize, int maxSize);
+    field public static final androidx.paging.PagingConfig.Companion! Companion;
+    field public static final int MAX_SIZE_UNBOUNDED = 2147483647; // 0x7fffffff
+    field public final boolean enablePlaceholders;
+    field public final int initialLoadSize;
+    field public final int maxSize;
+    field public final int pageSize;
+    field public final int prefetchDistance;
+  }
+
+  public static final class PagingConfig.Builder {
+    ctor public PagingConfig.Builder(int pageSize);
+    method public androidx.paging.PagingConfig build();
+    method public androidx.paging.PagingConfig.Builder setEnablePlaceholders(boolean enablePlaceholders);
+    method public androidx.paging.PagingConfig.Builder setInitialLoadSizeHint(@IntRange(from=1) int initialLoadSizeHint);
+    method public androidx.paging.PagingConfig.Builder setMaxSize(@IntRange(from=2) int maxSize);
+    method public androidx.paging.PagingConfig.Builder setPrefetchDistance(@IntRange(from=0) int prefetchDistance);
+  }
+
+  public static final class PagingConfig.Companion {
+  }
+
   public abstract class PositionalDataSource<T> extends androidx.paging.DataSource<java.lang.Integer,T> {
     ctor public PositionalDataSource();
     method public static final int computeInitialLoadPosition(androidx.paging.PositionalDataSource.LoadInitialParams params, int totalCount);
diff --git a/paging/common/api/public_plus_experimental_3.0.0-alpha01.txt b/paging/common/api/public_plus_experimental_3.0.0-alpha01.txt
index 4ef6eae..9dab050 100644
--- a/paging/common/api/public_plus_experimental_3.0.0-alpha01.txt
+++ b/paging/common/api/public_plus_experimental_3.0.0-alpha01.txt
@@ -143,8 +143,8 @@
   }
 
   public final class PagedDataFlowBuilder<Key, Value> {
-    ctor public PagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
-    ctor @Deprecated public PagedDataFlowBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public PagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagingConfig config);
+    ctor @Deprecated public PagedDataFlowBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagingConfig config);
     method public kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<Value>> build();
     method public androidx.paging.PagedDataFlowBuilder<Key,Value> setInitialKey(Key? initialKey);
   }
@@ -292,6 +292,29 @@
   public final class PagedSourceKt {
   }
 
+  public final class PagingConfig {
+    ctor public PagingConfig(int pageSize, int prefetchDistance, boolean enablePlaceholders, int initialLoadSize, int maxSize);
+    field public static final androidx.paging.PagingConfig.Companion! Companion;
+    field public static final int MAX_SIZE_UNBOUNDED = 2147483647; // 0x7fffffff
+    field public final boolean enablePlaceholders;
+    field public final int initialLoadSize;
+    field public final int maxSize;
+    field public final int pageSize;
+    field public final int prefetchDistance;
+  }
+
+  public static final class PagingConfig.Builder {
+    ctor public PagingConfig.Builder(int pageSize);
+    method public androidx.paging.PagingConfig build();
+    method public androidx.paging.PagingConfig.Builder setEnablePlaceholders(boolean enablePlaceholders);
+    method public androidx.paging.PagingConfig.Builder setInitialLoadSizeHint(@IntRange(from=1) int initialLoadSizeHint);
+    method public androidx.paging.PagingConfig.Builder setMaxSize(@IntRange(from=2) int maxSize);
+    method public androidx.paging.PagingConfig.Builder setPrefetchDistance(@IntRange(from=0) int prefetchDistance);
+  }
+
+  public static final class PagingConfig.Companion {
+  }
+
   public abstract class PositionalDataSource<T> extends androidx.paging.DataSource<java.lang.Integer,T> {
     ctor public PositionalDataSource();
     method public static final int computeInitialLoadPosition(androidx.paging.PositionalDataSource.LoadInitialParams params, int totalCount);
diff --git a/paging/common/api/public_plus_experimental_current.txt b/paging/common/api/public_plus_experimental_current.txt
index 4ef6eae..9dab050 100644
--- a/paging/common/api/public_plus_experimental_current.txt
+++ b/paging/common/api/public_plus_experimental_current.txt
@@ -143,8 +143,8 @@
   }
 
   public final class PagedDataFlowBuilder<Key, Value> {
-    ctor public PagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
-    ctor @Deprecated public PagedDataFlowBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public PagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagingConfig config);
+    ctor @Deprecated public PagedDataFlowBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagingConfig config);
     method public kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<Value>> build();
     method public androidx.paging.PagedDataFlowBuilder<Key,Value> setInitialKey(Key? initialKey);
   }
@@ -292,6 +292,29 @@
   public final class PagedSourceKt {
   }
 
+  public final class PagingConfig {
+    ctor public PagingConfig(int pageSize, int prefetchDistance, boolean enablePlaceholders, int initialLoadSize, int maxSize);
+    field public static final androidx.paging.PagingConfig.Companion! Companion;
+    field public static final int MAX_SIZE_UNBOUNDED = 2147483647; // 0x7fffffff
+    field public final boolean enablePlaceholders;
+    field public final int initialLoadSize;
+    field public final int maxSize;
+    field public final int pageSize;
+    field public final int prefetchDistance;
+  }
+
+  public static final class PagingConfig.Builder {
+    ctor public PagingConfig.Builder(int pageSize);
+    method public androidx.paging.PagingConfig build();
+    method public androidx.paging.PagingConfig.Builder setEnablePlaceholders(boolean enablePlaceholders);
+    method public androidx.paging.PagingConfig.Builder setInitialLoadSizeHint(@IntRange(from=1) int initialLoadSizeHint);
+    method public androidx.paging.PagingConfig.Builder setMaxSize(@IntRange(from=2) int maxSize);
+    method public androidx.paging.PagingConfig.Builder setPrefetchDistance(@IntRange(from=0) int prefetchDistance);
+  }
+
+  public static final class PagingConfig.Companion {
+  }
+
   public abstract class PositionalDataSource<T> extends androidx.paging.DataSource<java.lang.Integer,T> {
     ctor public PositionalDataSource();
     method public static final int computeInitialLoadPosition(androidx.paging.PositionalDataSource.LoadInitialParams params, int totalCount);
diff --git a/paging/common/api/restricted_3.0.0-alpha01.txt b/paging/common/api/restricted_3.0.0-alpha01.txt
index c8fbb86..2b8b961 100644
--- a/paging/common/api/restricted_3.0.0-alpha01.txt
+++ b/paging/common/api/restricted_3.0.0-alpha01.txt
@@ -166,14 +166,14 @@
     method public final void connect(kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<T>> flow, kotlinx.coroutines.CoroutineScope scope, androidx.paging.PresenterCallback callback);
     method public final operator T? get(int index);
     method public final int getSize();
-    method public abstract suspend Object! performDiff(androidx.paging.NullPaddedList<T> previous, androidx.paging.NullPaddedList<T> p, kotlin.coroutines.Continuation<? super kotlin.Unit> p1);
+    method public abstract suspend Object! performDiff(androidx.paging.NullPaddedList<T> previousList, androidx.paging.NullPaddedList<T> newList, java.util.Map<androidx.paging.LoadType,? extends androidx.paging.LoadState> newLoadStates, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public final void retry();
     property public final int size;
   }
 
   public final class PagedDataFlowBuilder<Key, Value> {
-    ctor public PagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
-    ctor @Deprecated public PagedDataFlowBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public PagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagingConfig config);
+    ctor @Deprecated public PagedDataFlowBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagingConfig config);
     method public kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<Value>> build();
     method public androidx.paging.PagedDataFlowBuilder<Key,Value> setInitialKey(Key? initialKey);
   }
@@ -345,6 +345,29 @@
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <Key> androidx.paging.PagedSource.LoadParams<Key> toRefreshLoadParams(androidx.paging.PagedList.Config, Key? key);
   }
 
+  public final class PagingConfig {
+    ctor public PagingConfig(int pageSize, int prefetchDistance, boolean enablePlaceholders, int initialLoadSize, int maxSize);
+    field public static final androidx.paging.PagingConfig.Companion! Companion;
+    field public static final int MAX_SIZE_UNBOUNDED = 2147483647; // 0x7fffffff
+    field public final boolean enablePlaceholders;
+    field public final int initialLoadSize;
+    field public final int maxSize;
+    field public final int pageSize;
+    field public final int prefetchDistance;
+  }
+
+  public static final class PagingConfig.Builder {
+    ctor public PagingConfig.Builder(int pageSize);
+    method public androidx.paging.PagingConfig build();
+    method public androidx.paging.PagingConfig.Builder setEnablePlaceholders(boolean enablePlaceholders);
+    method public androidx.paging.PagingConfig.Builder setInitialLoadSizeHint(@IntRange(from=1) int initialLoadSizeHint);
+    method public androidx.paging.PagingConfig.Builder setMaxSize(@IntRange(from=2) int maxSize);
+    method public androidx.paging.PagingConfig.Builder setPrefetchDistance(@IntRange(from=0) int prefetchDistance);
+  }
+
+  public static final class PagingConfig.Companion {
+  }
+
   public abstract class PositionalDataSource<T> extends androidx.paging.DataSource<java.lang.Integer,T> {
     ctor public PositionalDataSource();
     method public static final int computeInitialLoadPosition(androidx.paging.PositionalDataSource.LoadInitialParams params, int totalCount);
@@ -384,7 +407,7 @@
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public interface PresenterCallback {
     method public void onChanged(int position, int count);
-    method public void onInserted(int position, int count, java.util.Map<androidx.paging.LoadType,? extends androidx.paging.LoadState>? loadStates);
+    method public void onInserted(int position, int count);
     method public void onRemoved(int position, int count);
     method public void onStateUpdate(androidx.paging.LoadType loadType, androidx.paging.LoadState loadState);
   }
diff --git a/paging/common/api/restricted_current.txt b/paging/common/api/restricted_current.txt
index c8fbb86..2b8b961 100644
--- a/paging/common/api/restricted_current.txt
+++ b/paging/common/api/restricted_current.txt
@@ -166,14 +166,14 @@
     method public final void connect(kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<T>> flow, kotlinx.coroutines.CoroutineScope scope, androidx.paging.PresenterCallback callback);
     method public final operator T? get(int index);
     method public final int getSize();
-    method public abstract suspend Object! performDiff(androidx.paging.NullPaddedList<T> previous, androidx.paging.NullPaddedList<T> p, kotlin.coroutines.Continuation<? super kotlin.Unit> p1);
+    method public abstract suspend Object! performDiff(androidx.paging.NullPaddedList<T> previousList, androidx.paging.NullPaddedList<T> newList, java.util.Map<androidx.paging.LoadType,? extends androidx.paging.LoadState> newLoadStates, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public final void retry();
     property public final int size;
   }
 
   public final class PagedDataFlowBuilder<Key, Value> {
-    ctor public PagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
-    ctor @Deprecated public PagedDataFlowBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public PagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagingConfig config);
+    ctor @Deprecated public PagedDataFlowBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagingConfig config);
     method public kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<Value>> build();
     method public androidx.paging.PagedDataFlowBuilder<Key,Value> setInitialKey(Key? initialKey);
   }
@@ -345,6 +345,29 @@
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static <Key> androidx.paging.PagedSource.LoadParams<Key> toRefreshLoadParams(androidx.paging.PagedList.Config, Key? key);
   }
 
+  public final class PagingConfig {
+    ctor public PagingConfig(int pageSize, int prefetchDistance, boolean enablePlaceholders, int initialLoadSize, int maxSize);
+    field public static final androidx.paging.PagingConfig.Companion! Companion;
+    field public static final int MAX_SIZE_UNBOUNDED = 2147483647; // 0x7fffffff
+    field public final boolean enablePlaceholders;
+    field public final int initialLoadSize;
+    field public final int maxSize;
+    field public final int pageSize;
+    field public final int prefetchDistance;
+  }
+
+  public static final class PagingConfig.Builder {
+    ctor public PagingConfig.Builder(int pageSize);
+    method public androidx.paging.PagingConfig build();
+    method public androidx.paging.PagingConfig.Builder setEnablePlaceholders(boolean enablePlaceholders);
+    method public androidx.paging.PagingConfig.Builder setInitialLoadSizeHint(@IntRange(from=1) int initialLoadSizeHint);
+    method public androidx.paging.PagingConfig.Builder setMaxSize(@IntRange(from=2) int maxSize);
+    method public androidx.paging.PagingConfig.Builder setPrefetchDistance(@IntRange(from=0) int prefetchDistance);
+  }
+
+  public static final class PagingConfig.Companion {
+  }
+
   public abstract class PositionalDataSource<T> extends androidx.paging.DataSource<java.lang.Integer,T> {
     ctor public PositionalDataSource();
     method public static final int computeInitialLoadPosition(androidx.paging.PositionalDataSource.LoadInitialParams params, int totalCount);
@@ -384,7 +407,7 @@
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public interface PresenterCallback {
     method public void onChanged(int position, int count);
-    method public void onInserted(int position, int count, java.util.Map<androidx.paging.LoadType,? extends androidx.paging.LoadState>? loadStates);
+    method public void onInserted(int position, int count);
     method public void onRemoved(int position, int count);
     method public void onStateUpdate(androidx.paging.LoadType loadType, androidx.paging.LoadState loadState);
   }
diff --git a/paging/common/build.gradle b/paging/common/build.gradle
index 39ea933..831d468 100644
--- a/paging/common/build.gradle
+++ b/paging/common/build.gradle
@@ -38,6 +38,7 @@
     testImplementation MOCKITO_KOTLIN, {
         exclude group: 'org.mockito' // to keep control on the mockito version
     }
+    testImplementation project(':paging:paging-testutils')
     testImplementation project(':internal-testutils-common')
     testImplementation project(':internal-testutils-ktx')
     testImplementation(KOTLIN_TEST)
diff --git a/paging/common/src/main/kotlin/androidx/paging/PageFetcher.kt b/paging/common/src/main/kotlin/androidx/paging/PageFetcher.kt
index f44ac5b..1bc88cb 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PageFetcher.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PageFetcher.kt
@@ -29,7 +29,7 @@
 internal class PageFetcher<Key : Any, Value : Any>(
     private val pagedSourceFactory: () -> PagedSource<Key, Value>,
     private val initialKey: Key?,
-    private val config: PagedList.Config
+    private val config: PagingConfig
 ) {
     // NOTE: This channel is conflated, which means it has a buffer size of 1, and will always
     // broadcast the latest value received.
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagePresenter.kt b/paging/common/src/main/kotlin/androidx/paging/PagePresenter.kt
index aecb2cb..e3b0b8b 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagePresenter.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagePresenter.kt
@@ -23,11 +23,19 @@
 import androidx.paging.LoadType.START
 import androidx.paging.PageEvent.Insert.Companion.Refresh
 
-/** @hide */
+/**
+ * Callbacks for the presenter/adapter to listen to the state of pagination data.
+ *
+ * Note that these won't map directly to PageEvents, since PageEvents can cause several adapter
+ * events that should all be dispatched to the presentation layer at once - as part of the same
+ * frame.
+ *
+ * @hide
+ */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 interface PresenterCallback {
     fun onChanged(position: Int, count: Int)
-    fun onInserted(position: Int, count: Int, loadStates: Map<LoadType, LoadState>?)
+    fun onInserted(position: Int, count: Int)
     fun onRemoved(position: Int, count: Int)
     fun onStateUpdate(loadType: LoadType, loadState: LoadState)
 }
@@ -153,10 +161,10 @@
 
                 // ... then trigger callbacks, so callbacks won't see inconsistent state
                 callback.onChanged(placeholdersChangedPos, placeholdersChangedCount)
-                callback.onInserted(itemsInsertedPos, itemsInsertedCount, insert.loadStates)
+                callback.onInserted(itemsInsertedPos, itemsInsertedCount)
                 val placeholderInsertedCount = size - oldSize - itemsInsertedCount
                 if (placeholderInsertedCount > 0) {
-                    callback.onInserted(0, placeholderInsertedCount, insert.loadStates)
+                    callback.onInserted(0, placeholderInsertedCount)
                 } else if (placeholderInsertedCount < 0) {
                     callback.onRemoved(0, -placeholderInsertedCount)
                 }
@@ -175,19 +183,19 @@
 
                 // ... then trigger callbacks, so callbacks won't see inconsistent state
                 callback.onChanged(placeholdersChangedPos, placeholdersChangedCount)
-                callback.onInserted(itemsInsertedPos, itemsInsertedCount, insert.loadStates)
+                callback.onInserted(itemsInsertedPos, itemsInsertedCount)
                 val placeholderInsertedCount = size - oldSize - itemsInsertedCount
                 if (placeholderInsertedCount > 0) {
                     callback.onInserted(
                         position = size - placeholderInsertedCount,
-                        count = placeholderInsertedCount,
-                        loadStates = insert.loadStates
+                        count = placeholderInsertedCount
                     )
                 } else if (placeholderInsertedCount < 0) {
                     callback.onRemoved(size, -placeholderInsertedCount)
                 }
             }
         }
+        insert.loadStates.entries.forEach { callback.onStateUpdate(it.key, it.value) }
     }
 
     private fun dropPages(drop: PageEvent.Drop<T>, callback: PresenterCallback) {
@@ -213,10 +221,13 @@
             callback.onRemoved(itemsRemovedPos, itemsRemovedCount)
             val placeholderInsertedCount = size - oldSize + itemsRemovedCount
             if (placeholderInsertedCount > 0) {
-                callback.onInserted(0, placeholderInsertedCount, null)
+                callback.onInserted(0, placeholderInsertedCount)
             } else if (placeholderInsertedCount < 0) {
                 callback.onRemoved(0, -placeholderInsertedCount)
             }
+
+            // dropping from start implies start is idle
+            callback.onStateUpdate(START, Idle)
         } else {
             val removeCount = pages.takeLast(drop.count).fullCount()
 
@@ -238,10 +249,13 @@
             callback.onRemoved(itemsRemovedPos, itemsRemovedCount)
             val placeholderInsertedCount = size - oldSize + itemsRemovedCount
             if (placeholderInsertedCount > 0) {
-                callback.onInserted(size, placeholderInsertedCount, null)
+                callback.onInserted(size, placeholderInsertedCount)
             } else if (placeholderInsertedCount < 0) {
                 callback.onRemoved(size, -placeholderInsertedCount)
             }
+
+            // dropping from end implies end is idle
+            callback.onStateUpdate(END, Idle)
         }
     }
 
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedDataDiffer.kt b/paging/common/src/main/kotlin/androidx/paging/PagedDataDiffer.kt
index 5bc3457..1be1bbf 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedDataDiffer.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedDataDiffer.kt
@@ -39,7 +39,11 @@
     private var presenter: PagePresenter<T> = PagePresenter.initial()
     private var receiver: UiReceiver? = null
 
-    abstract suspend fun performDiff(previous: NullPaddedList<T>, new: NullPaddedList<T>)
+    abstract suspend fun performDiff(
+        previousList: NullPaddedList<T>,
+        newList: NullPaddedList<T>,
+        newLoadStates: Map<LoadType, LoadState>
+    )
 
     @UseExperimental(ExperimentalCoroutinesApi::class)
     fun connect(flow: Flow<PagedData<T>>, scope: CoroutineScope, callback: PresenterCallback) {
@@ -54,7 +58,11 @@
                         val event = pair.second
                         if (event is PageEvent.Insert && event.loadType == LoadType.REFRESH) {
                             val newPresenter = PagePresenter(event)
-                            performDiff(previous = presenter, new = newPresenter)
+                            performDiff(
+                                previousList = presenter,
+                                newList = newPresenter,
+                                newLoadStates = event.loadStates
+                            )
                             presenter = newPresenter
                             receiver = pair.first.receiver
                         } else {
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedDataFlowBuilder.kt b/paging/common/src/main/kotlin/androidx/paging/PagedDataFlowBuilder.kt
index dfba9f2..7a57944 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedDataFlowBuilder.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedDataFlowBuilder.kt
@@ -26,12 +26,12 @@
  */
 class PagedDataFlowBuilder<Key : Any, Value : Any>(
     private val pagedSourceFactory: () -> PagedSource<Key, Value>,
-    private val config: PagedList.Config
+    private val config: PagingConfig
 ) {
     @Deprecated("DataSource is deprecated and has been replaced by PagedSource")
     constructor(
         dataSourceFactory: DataSource.Factory<Key, Value>,
-        config: PagedList.Config
+        config: PagingConfig
     ) : this(dataSourceFactory.asPagedSourceFactory(), config)
 
     private var initialKey: Key? = null
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
index 3352168..2cffd56 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
@@ -366,7 +366,7 @@
         ) : this(
             pagedSource = pagedSource,
             initialPage = initialPage,
-            config = PagedList.Config.Builder().setPageSize(pageSize).build()
+            config = Builder().setPageSize(pageSize).build()
         )
 
         /**
@@ -553,7 +553,7 @@
      * Configures how a [PagedList] loads content from its [PagedSource].
      *
      * Use [Config.Builder] to construct and define custom loading behavior, such as
-     * [Builder.setPageSize], which defines number of items loaded at a time}.
+     * [Builder.setPageSize], which defines number of items loaded at a time.
      */
     class Config internal constructor(
         /**
diff --git a/paging/common/src/main/kotlin/androidx/paging/Pager.kt b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
index a49c962..0bf290f 100644
--- a/paging/common/src/main/kotlin/androidx/paging/Pager.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
@@ -59,7 +59,7 @@
 internal class Pager<Key : Any, Value : Any>(
     internal val initialKey: Key?,
     internal val pagedSource: PagedSource<Key, Value>,
-    private val config: PagedList.Config
+    private val config: PagingConfig
 ) {
     private val retryChannel = Channel<Unit>(CONFLATED)
     private val hintChannel = BroadcastChannel<ViewportHint>(CONFLATED)
@@ -203,7 +203,7 @@
         LoadParams(
             loadType = loadType,
             key = key,
-            loadSize = if (loadType == REFRESH) config.initialLoadSizeHint else config.pageSize,
+            loadSize = if (loadType == REFRESH) config.initialLoadSize else config.pageSize,
             placeholdersEnabled = config.enablePlaceholders,
             pageSize = config.pageSize
         )
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagingConfig.kt b/paging/common/src/main/kotlin/androidx/paging/PagingConfig.kt
new file mode 100644
index 0000000..de4ee7a
--- /dev/null
+++ b/paging/common/src/main/kotlin/androidx/paging/PagingConfig.kt
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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 androidx.annotation.IntRange
+
+/**
+ * Configures how to load content from a [PagedSource].
+ *
+ * Use [PagingConfig.Builder] to construct and define custom loading behavior, such as
+ * [pageSize], which defines number of items loaded at a time.
+ */
+class PagingConfig(
+    /**
+     * Defines the number of items loaded at once from the [PagedSource].
+     *
+     * Should be several times the number of visible items onscreen.
+     *
+     * Configuring your page size depends on how your data is being loaded and used. Smaller
+     * page sizes improve memory usage, latency, and avoid GC churn. Larger pages generally
+     * improve loading throughput, to a point (avoid loading more than 2MB from SQLite at
+     * once, since it incurs extra cost).
+     *
+     * If you're loading data for very large, social-media style cards that take up most of
+     * a screen, and your database isn't a bottleneck, 10-20 may make sense. If you're
+     * displaying dozens of items in a tiled grid, which can present items during a scroll
+     * much more quickly, consider closer to 100.
+     */
+    @JvmField
+    val pageSize: Int,
+    /**
+     * Prefetch distance which defines how far ahead to load.
+     *
+     * If this value is set to 50, the paged list will attempt to load 50 items in advance of
+     * data that's already been accessed.
+     *
+     * @see PagedList.loadAround
+     */
+    @JvmField
+    val prefetchDistance: Int = pageSize,
+    /**
+     * Defines whether the [PagedList] may display null placeholders, if the [PagedSource]
+     * provides them.
+     */
+    @JvmField
+    val enablePlaceholders: Boolean = true,
+
+    /**
+     * Size requested size for initial load of from [PagedSource], generally larger than a regular
+     * page.
+     */
+    @JvmField
+    val initialLoadSize: Int = pageSize * DEFAULT_INITIAL_PAGE_MULTIPLIER,
+    /**
+     * Defines the maximum number of items that may be loaded into this pagedList before pages
+     * should be dropped.
+     *
+     * If set to [MAX_SIZE_UNBOUNDED], pages will never be dropped.
+     *
+     * @see MAX_SIZE_UNBOUNDED
+     * @see Builder.setMaxSize
+     */
+    @JvmField
+    val maxSize: Int = MAX_SIZE_UNBOUNDED
+) {
+    init {
+        if (!enablePlaceholders && prefetchDistance == 0) {
+            throw IllegalArgumentException(
+                "Placeholders and prefetch are the only ways" +
+                        " to trigger loading of more data in the PagedList, so either" +
+                        " placeholders must be enabled, or prefetch distance must be > 0."
+            )
+        }
+        if (maxSize != MAX_SIZE_UNBOUNDED && maxSize < pageSize + prefetchDistance * 2) {
+            throw IllegalArgumentException(
+                "Maximum size must be at least pageSize + 2*prefetchDist" +
+                        ", pageSize=$pageSize, prefetchDist=$prefetchDistance" +
+                        ", maxSize=$maxSize"
+            )
+        }
+    }
+    companion object {
+        /**
+         * When [maxSize] is set to [MAX_SIZE_UNBOUNDED], the maximum number of items loaded is
+         * unbounded, and pages will never be dropped.
+         */
+        const val MAX_SIZE_UNBOUNDED = Int.MAX_VALUE
+        internal const val DEFAULT_INITIAL_PAGE_MULTIPLIER = 3
+    }
+
+    /**
+     * Builder class for [PagingConfig] for Java callers.
+     *
+     * Kotlin callers should use the Config constructor directly.
+     */
+    class Builder(
+        private val pageSize: Int
+    ) {
+        private var prefetchDistance = -1
+        private var initialLoadSizeHint = -1
+        private var enablePlaceholders = true
+        private var maxSize = MAX_SIZE_UNBOUNDED
+
+        /**
+         * Defines how far from the edge of loaded content an access must be to trigger further
+         * loading.
+         *
+         * Should be several times the number of visible items onscreen.
+         *
+         * If not set, defaults to page size.
+         *
+         * A value of 0 indicates that no list items will be loaded until they are specifically
+         * requested. This is generally not recommended, so that users don't observe a
+         * placeholder item (with placeholders) or end of list (without) while scrolling.
+         *
+         * @param prefetchDistance Distance the [PagedList] should prefetch.
+         * @return this
+         */
+        fun setPrefetchDistance(@IntRange(from = 0) prefetchDistance: Int) = apply {
+            this.prefetchDistance = prefetchDistance
+        }
+
+        /**
+         * Pass false to disable null placeholders in [PagedList]s using this [PagingConfig].
+         *
+         * If not set, defaults to true.
+         *
+         * A [PagedList] will present null placeholders for not-yet-loaded content if two
+         * conditions are met:
+         *
+         * 1) Its [PagedSource] can count all unloaded items (so that the number of nulls to
+         * present is known).
+         *
+         * 2) placeholders are not disabled on the [PagingConfig].
+         *
+         * Call `setEnablePlaceholders(false)` to ensure the receiver of the PagedList
+         * (often a [androidx.paging.PagedDataAdapter]) doesn't need to account for null items.
+         *
+         * If placeholders are disabled, not-yet-loaded content will not be present in the list.
+         * Paging will still occur, but as items are loaded or removed, they will be signaled
+         * as inserts to the [PagedList.Callback].
+         *
+         * [PagedList.Callback.onChanged] will not be issued as part of loading, though a
+         * [androidx.paging.PagedDataAdapter] may still receive change events as a result of
+         * [PagedList] diffing.
+         *
+         * @param enablePlaceholders `false` if null placeholders should be disabled.
+         * @return this
+         */
+        fun setEnablePlaceholders(enablePlaceholders: Boolean) = apply {
+            this.enablePlaceholders = enablePlaceholders
+        }
+
+        /**
+         * Defines how many items to load when first load occurs.
+         *
+         * This value is typically larger than page size, so on first load data there's a large
+         * enough range of content loaded to cover small scrolls.
+         *
+         * If not set, defaults to three times page size.
+         *
+         * @param initialLoadSizeHint Number of items to load while initializing the [PagedList]
+         * @return this
+         */
+        fun setInitialLoadSizeHint(@IntRange(from = 1) initialLoadSizeHint: Int) = apply {
+            this.initialLoadSizeHint = initialLoadSizeHint
+        }
+
+        /**
+         * Defines how many items to keep loaded at once.
+         *
+         * This can be used to cap the number of items kept in memory by dropping pages. This
+         * value is typically many pages so old pages are cached in case the user scrolls back.
+         *
+         * This value must be at least two times the [prefetchDistance] plus the [pageSize]).
+         * This constraint prevent loads from being continuously fetched and discarded due to
+         * prefetching.
+         *
+         * The max size specified here best effort, not a guarantee. In practice, if [maxSize]
+         * is many times the page size, the number of items held by the [PagedList] will not
+         * grow above this number. Exceptions are made as necessary to guarantee:
+         *  * Pages are never dropped until there are more than two pages loaded. Note that
+         * a [PagedSource] may not be held strictly to [requested pageSize][PagingConfig.pageSize], so
+         * two pages may be larger than expected.
+         *  * Pages are never dropped if they are within a prefetch window (defined to be
+         * `pageSize + (2 * prefetchDistance)`) of the most recent load.
+         *
+         * If not set, defaults to [MAX_SIZE_UNBOUNDED], which disables page dropping.
+         *
+         * @param maxSize Maximum number of items to keep in memory, or [MAX_SIZE_UNBOUNDED] to
+         * disable page dropping.
+         * @return this
+         *
+         * @see PagingConfig.MAX_SIZE_UNBOUNDED
+         * @see PagingConfig.maxSize
+         */
+        fun setMaxSize(@IntRange(from = 2) maxSize: Int) = apply {
+            this.maxSize = maxSize
+        }
+
+        /**
+         * Creates a [PagingConfig] with the given parameters.
+         *
+         * @return A new [PagingConfig].
+         */
+        fun build(): PagingConfig {
+            if (prefetchDistance < 0) {
+                prefetchDistance = pageSize
+            }
+            if (initialLoadSizeHint < 0) {
+                initialLoadSizeHint = pageSize * DEFAULT_INITIAL_PAGE_MULTIPLIER
+            }
+
+            return PagingConfig(
+                pageSize,
+                prefetchDistance,
+                enablePlaceholders,
+                initialLoadSizeHint,
+                maxSize
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/paging/common/src/test/kotlin/androidx/paging/CachingTest.kt b/paging/common/src/test/kotlin/androidx/paging/CachingTest.kt
index 7c4f3a5..46f3798 100644
--- a/paging/common/src/test/kotlin/androidx/paging/CachingTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/CachingTest.kt
@@ -292,11 +292,11 @@
     private fun buildPageFlow(): Flow<PagedData<Item>> {
         return PagedDataFlowBuilder(
             pagedSourceFactory = StringPagedSource.VersionedFactory()::create,
-            config = PagedList.Config(
+            config = PagingConfig(
                 pageSize = 3,
                 prefetchDistance = 1,
                 enablePlaceholders = false,
-                initialLoadSizeHint = 3,
+                initialLoadSize = 3,
                 maxSize = 1000
             )
         ).build()
diff --git a/paging/common/src/test/kotlin/androidx/paging/PageFetcherTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageFetcherTest.kt
index fa23d76..43339e9 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PageFetcherTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageFetcherTest.kt
@@ -41,11 +41,11 @@
 class PageFetcherTest {
     private val testScope = TestCoroutineScope()
     private val pagedSourceFactory = { TestPagedSource() }
-    private val config = PagedList.Config(
+    private val config = PagingConfig(
         pageSize = 1,
         prefetchDistance = 1,
         enablePlaceholders = true,
-        initialLoadSizeHint = 2,
+        initialLoadSize = 2,
         maxSize = 3
     )
 
@@ -168,29 +168,3 @@
 
     return FetcherState(pagedDataList, pageEventLists, job)
 }
-
-internal fun assertEvents(expected: List<PageEvent<Int>>, actual: List<PageEvent<Int>>) {
-    try {
-        assertEquals(expected, actual)
-    } catch (e: Throwable) {
-        val msg = e.localizedMessage
-            .replace("),", "),\n")
-            .replace("<[", "<[\n ")
-            .replace("actual", "\nactual")
-            .lines()
-            .toMutableList()
-
-        if (expected.count() != actual.count()) throw AssertionError(msg.joinToString("\n"))
-
-        var index = 0
-        for (i in 0 until expected.count()) {
-            if (expected[i] != actual[i]) {
-                index = i
-                break
-            }
-        }
-        msg[index + 1] = msg[index + 1].prependIndent(" >")
-        msg[msg.count() / 2 + index + 1] = msg[msg.count() / 2 + index + 1].prependIndent(" >")
-        throw AssertionError(msg.joinToString("\n"))
-    }
-}
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagePresenterTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagePresenterTest.kt
index 8e22b85..6a1547f 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagePresenterTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagePresenterTest.kt
@@ -118,7 +118,7 @@
         assertEquals(expectedData, data.asList())
 
         // ... then assert events
-        assertEquals(events, callback.getAllAndClear())
+        assertEquals(events + IDLE_EVENTS, callback.getAllAndClear())
     }
 
     private fun verifyPrepend(
@@ -155,7 +155,7 @@
         assertEquals(expectedData, data.asList())
 
         // ... then assert events
-        assertEquals(events, callback.getAllAndClear())
+        assertEquals(events + IDLE_EVENTS, callback.getAllAndClear())
     }
 
     private fun verifyPrependAppend(
@@ -310,7 +310,7 @@
         val callback = PresenterCallbackCapture()
         data.dropPages(false, pagesToDrop, newNulls, callback)
 
-        assertEquals(events, callback.getAllAndClear())
+        assertEquals(events + listOf(StateEvent(END, Idle)), callback.getAllAndClear())
 
         // assert final list state
         val finalData = initialPages.subList(0, initialPages.size - pagesToDrop).flatten()
@@ -343,7 +343,7 @@
         val callback = PresenterCallbackCapture()
         data.dropPages(true, pagesToDrop, newNulls, callback)
 
-        assertEquals(events, callback.getAllAndClear())
+        assertEquals(events + listOf(StateEvent(START, Idle)), callback.getAllAndClear())
 
         // assert final list state
         val finalData = initialPages.take(initialPages.size - pagesToDrop).reversed().flatten()
@@ -544,4 +544,12 @@
         assertEquals(ViewportHint(1, 1), pagePresenter.loadAround(5))
         assertEquals(ViewportHint(1, 2), pagePresenter.loadAround(6))
     }
+
+    companion object {
+        val IDLE_EVENTS = listOf<PresenterEvent>(
+            StateEvent(REFRESH, Idle),
+            StateEvent(START, Idle),
+            StateEvent(END, Idle)
+        )
+    }
 }
\ No newline at end of file
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
index 65a1a98..5179687 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagerTest.kt
@@ -48,11 +48,11 @@
 class PagerTest {
     private val testScope = TestCoroutineScope()
     private val pagedSourceFactory = { TestPagedSource() }
-    private val config = PagedList.Config(
+    private val config = PagingConfig(
         pageSize = 1,
         prefetchDistance = 1,
         enablePlaceholders = true,
-        initialLoadSizeHint = 2,
+        initialLoadSize = 2,
         maxSize = 3
     )
 
@@ -301,11 +301,11 @@
 
     @Test
     fun prependMultiplePages() = testScope.runBlockingTest {
-        val config = PagedList.Config(
+        val config = PagingConfig(
             pageSize = 1,
             prefetchDistance = 2,
             enablePlaceholders = true,
-            initialLoadSizeHint = 3,
+            initialLoadSize = 3,
             maxSize = 5
         )
         val pageFetcher = PageFetcher(pagedSourceFactory, 50, config)
@@ -353,11 +353,11 @@
 
     @Test
     fun appendMultiplePages() = testScope.runBlockingTest {
-        val config = PagedList.Config(
+        val config = PagingConfig(
             pageSize = 1,
             prefetchDistance = 2,
             enablePlaceholders = true,
-            initialLoadSizeHint = 3,
+            initialLoadSize = 3,
             maxSize = 5
         )
         val pageFetcher = PageFetcher(pagedSourceFactory, 50, config)
@@ -444,46 +444,6 @@
     }
 
     @Test
-    fun competingPrependAndAppendWithDropping() = testScope.runBlockingTest {
-        pauseDispatcher {
-            val config = PagedList.Config(
-                pageSize = 1,
-                prefetchDistance = 2,
-                enablePlaceholders = true,
-                initialLoadSizeHint = 1,
-                maxSize = 3
-            )
-            val pageFetcher = PageFetcher(pagedSourceFactory, 50, config)
-            val fetcherState = collectFetcherState(pageFetcher)
-
-            advanceUntilIdle()
-            fetcherState.pagedDataList[0].receiver.addHint(ViewportHint(0, 0))
-            advanceUntilIdle()
-
-            val expected: List<PageEvent<Int>> = listOf(
-                StateUpdate(REFRESH, Loading),
-                StateUpdate(REFRESH, Done),
-                createRefresh(range = 50..50),
-                StateUpdate(START, Loading),
-                StateUpdate(END, Loading),
-                createPrepend(
-                    pageOffset = -1, range = 49..49, startState = Loading, endState = Loading
-                ),
-                createAppend(
-                    pageOffset = 1, range = 51..51, startState = Loading, endState = Loading
-                ),
-                StateUpdate(END, Idle),
-                Drop(END, 1, 49),
-                StateUpdate(START, Idle),
-                createPrepend(pageOffset = -2, range = 48..48, startState = Idle, endState = Idle)
-            )
-
-            assertEvents(expected, fetcherState.pageEventLists[0])
-            fetcherState.job.cancel()
-        }
-    }
-
-    @Test
     fun invalidateNoScroll() = testScope.runBlockingTest {
         val pageFetcher = PageFetcher(pagedSourceFactory, 50, config)
         val fetcherState = collectFetcherState(pageFetcher)
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagingConfigTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagingConfigTest.kt
new file mode 100644
index 0000000..bcae8c8
--- /dev/null
+++ b/paging/common/src/test/kotlin/androidx/paging/PagingConfigTest.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.paging
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertFailsWith
+
+@RunWith(JUnit4::class)
+class PagingConfigTest {
+    @Test
+    fun defaults() {
+        val config = PagingConfig(10)
+        assertEquals(10, config.pageSize)
+        assertEquals(30, config.initialLoadSize)
+        assertEquals(true, config.enablePlaceholders)
+        assertEquals(10, config.prefetchDistance)
+        assertEquals(PagedList.Config.MAX_SIZE_UNBOUNDED, config.maxSize)
+    }
+
+    @Test
+    fun requirePlaceholdersOrPrefetch() {
+        assertFailsWith<IllegalArgumentException> {
+            PagingConfig(
+                pageSize = 10,
+                enablePlaceholders = false,
+                prefetchDistance = 0
+            )
+        }
+    }
+
+    @Test
+    fun prefetchWindowMustFitInMaxSize() {
+        assertFailsWith<IllegalArgumentException> {
+            PagingConfig(
+                pageSize = 3,
+                prefetchDistance = 4,
+                maxSize = 10
+            )
+        }
+    }
+}
diff --git a/paging/common/src/test/kotlin/androidx/paging/PresenterCallbackCapture.kt b/paging/common/src/test/kotlin/androidx/paging/PresenterCallbackCapture.kt
index cb0ff04..ff7bf16 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PresenterCallbackCapture.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PresenterCallbackCapture.kt
@@ -32,7 +32,7 @@
         }
     }
 
-    override fun onInserted(position: Int, count: Int, loadStates: Map<LoadType, LoadState>?) {
+    override fun onInserted(position: Int, count: Int) {
         if (count != 0) {
             list.add(InsertEvent(position, count))
         }
diff --git a/paging/integration-tests/testapp/build.gradle b/paging/integration-tests/testapp/build.gradle
index f99f782..ae3c6f0 100644
--- a/paging/integration-tests/testapp/build.gradle
+++ b/paging/integration-tests/testapp/build.gradle
@@ -17,13 +17,6 @@
 import static androidx.build.dependencies.DependenciesKt.MULTIDEX
 import static androidx.build.dependencies.DependenciesKt.getKOTLIN_STDLIB
 
-buildscript {
-    // TODO: Remove this when this test app no longer depends on 1.0.0 of vectordrawable-animated.
-    // vectordrawable and vectordrawable-animated were accidentally using the same package name
-    // which is no longer valid in namespaced resource world.
-    project.ext['android.uniquePackageNames'] = false
-}
-
 plugins {
     id("AndroidXPlugin")
     id("com.android.application")
@@ -32,18 +25,18 @@
 }
 
 dependencies {
-    implementation("androidx.arch.core:core-runtime:2.0.1")
-    implementation("androidx.room:room-runtime:2.0.0")
-    implementation("androidx.room:room-rxjava2:2.0.0")
+    implementation("androidx.arch.core:core-runtime:2.1.0")
+    implementation("androidx.room:room-runtime:2.2.3")
+    implementation("androidx.room:room-rxjava2:2.2.3")
 
     implementation(project(":paging:paging-common-ktx"))
     implementation(project(":paging:paging-runtime"))
     implementation(project(":paging:paging-rxjava2"))
 
-    kapt("androidx.room:room-compiler:2.0.0")
+    kapt("androidx.room:room-compiler:2.2.3")
 
     implementation(MULTIDEX)
-    implementation("androidx.recyclerview:recyclerview:1.0.0")
+    implementation("androidx.recyclerview:recyclerview:1.1.0")
     implementation(project(":fragment:fragment-ktx"))
     implementation("androidx.appcompat:appcompat:1.1.0")
     implementation(KOTLIN_STDLIB)
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/V3ViewModel.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/V3ViewModel.kt
index 75b7a97..6ddad40 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/V3ViewModel.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/v3/V3ViewModel.kt
@@ -19,8 +19,8 @@
 import android.graphics.Color
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
-import androidx.paging.Config
 import androidx.paging.PagedDataFlowBuilder
+import androidx.paging.PagingConfig
 import androidx.paging.cachedIn
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.FlowPreview
@@ -30,7 +30,7 @@
 class V3ViewModel : ViewModel() {
     @FlowPreview
     val flow =
-        PagedDataFlowBuilder(ItemPagedSource.Factory, Config(pageSize = 10))
+        PagedDataFlowBuilder(ItemPagedSource.Factory, PagingConfig(pageSize = 10))
             .build()
             .map {
                 it.insertSeparators { before: Item?, after: Item? ->
diff --git a/paging/runtime/api/3.0.0-alpha01.txt b/paging/runtime/api/3.0.0-alpha01.txt
index 37e07ce..2e8c621 100644
--- a/paging/runtime/api/3.0.0-alpha01.txt
+++ b/paging/runtime/api/3.0.0-alpha01.txt
@@ -61,26 +61,24 @@
 
   public abstract class PagedDataAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
     ctor public PagedDataAdapter(androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback, kotlinx.coroutines.CoroutineDispatcher mainDispatcher);
-    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public final void connect(kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<T>> flow, kotlinx.coroutines.CoroutineScope scope);
     method protected T? getItem(int position);
     method public int getItemCount();
-    method public void onLoadStateChanged(androidx.paging.LoadType type, androidx.paging.LoadState state);
-    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public final void retry();
   }
 
   public abstract class PagedListAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
     ctor protected PagedListAdapter(androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback);
     ctor protected PagedListAdapter(androidx.recyclerview.widget.AsyncDifferConfig<T> config);
-    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public androidx.paging.PagedList<T>? getCurrentList();
     method protected T? getItem(int position);
     method public int getItemCount();
     method @Deprecated public void onCurrentListChanged(androidx.paging.PagedList<T>? currentList);
     method public void onCurrentListChanged(androidx.paging.PagedList<T>? previousList, androidx.paging.PagedList<T>? currentList);
-    method public void onLoadStateChanged(androidx.paging.LoadType type, androidx.paging.LoadState state);
-    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public void submitList(androidx.paging.PagedList<T>? pagedList);
     method public void submitList(androidx.paging.PagedList<T>? pagedList, Runnable? commitCallback);
     property public androidx.paging.PagedList<T>? currentList;
diff --git a/paging/runtime/api/current.txt b/paging/runtime/api/current.txt
index 37e07ce..2e8c621 100644
--- a/paging/runtime/api/current.txt
+++ b/paging/runtime/api/current.txt
@@ -61,26 +61,24 @@
 
   public abstract class PagedDataAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
     ctor public PagedDataAdapter(androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback, kotlinx.coroutines.CoroutineDispatcher mainDispatcher);
-    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public final void connect(kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<T>> flow, kotlinx.coroutines.CoroutineScope scope);
     method protected T? getItem(int position);
     method public int getItemCount();
-    method public void onLoadStateChanged(androidx.paging.LoadType type, androidx.paging.LoadState state);
-    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public final void retry();
   }
 
   public abstract class PagedListAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
     ctor protected PagedListAdapter(androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback);
     ctor protected PagedListAdapter(androidx.recyclerview.widget.AsyncDifferConfig<T> config);
-    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public androidx.paging.PagedList<T>? getCurrentList();
     method protected T? getItem(int position);
     method public int getItemCount();
     method @Deprecated public void onCurrentListChanged(androidx.paging.PagedList<T>? currentList);
     method public void onCurrentListChanged(androidx.paging.PagedList<T>? previousList, androidx.paging.PagedList<T>? currentList);
-    method public void onLoadStateChanged(androidx.paging.LoadType type, androidx.paging.LoadState state);
-    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public void submitList(androidx.paging.PagedList<T>? pagedList);
     method public void submitList(androidx.paging.PagedList<T>? pagedList, Runnable? commitCallback);
     property public androidx.paging.PagedList<T>? currentList;
diff --git a/paging/runtime/api/public_plus_experimental_3.0.0-alpha01.txt b/paging/runtime/api/public_plus_experimental_3.0.0-alpha01.txt
index 37e07ce..2e8c621 100644
--- a/paging/runtime/api/public_plus_experimental_3.0.0-alpha01.txt
+++ b/paging/runtime/api/public_plus_experimental_3.0.0-alpha01.txt
@@ -61,26 +61,24 @@
 
   public abstract class PagedDataAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
     ctor public PagedDataAdapter(androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback, kotlinx.coroutines.CoroutineDispatcher mainDispatcher);
-    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public final void connect(kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<T>> flow, kotlinx.coroutines.CoroutineScope scope);
     method protected T? getItem(int position);
     method public int getItemCount();
-    method public void onLoadStateChanged(androidx.paging.LoadType type, androidx.paging.LoadState state);
-    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public final void retry();
   }
 
   public abstract class PagedListAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
     ctor protected PagedListAdapter(androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback);
     ctor protected PagedListAdapter(androidx.recyclerview.widget.AsyncDifferConfig<T> config);
-    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public androidx.paging.PagedList<T>? getCurrentList();
     method protected T? getItem(int position);
     method public int getItemCount();
     method @Deprecated public void onCurrentListChanged(androidx.paging.PagedList<T>? currentList);
     method public void onCurrentListChanged(androidx.paging.PagedList<T>? previousList, androidx.paging.PagedList<T>? currentList);
-    method public void onLoadStateChanged(androidx.paging.LoadType type, androidx.paging.LoadState state);
-    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public void submitList(androidx.paging.PagedList<T>? pagedList);
     method public void submitList(androidx.paging.PagedList<T>? pagedList, Runnable? commitCallback);
     property public androidx.paging.PagedList<T>? currentList;
diff --git a/paging/runtime/api/public_plus_experimental_current.txt b/paging/runtime/api/public_plus_experimental_current.txt
index 37e07ce..2e8c621 100644
--- a/paging/runtime/api/public_plus_experimental_current.txt
+++ b/paging/runtime/api/public_plus_experimental_current.txt
@@ -61,26 +61,24 @@
 
   public abstract class PagedDataAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
     ctor public PagedDataAdapter(androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback, kotlinx.coroutines.CoroutineDispatcher mainDispatcher);
-    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public final void connect(kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<T>> flow, kotlinx.coroutines.CoroutineScope scope);
     method protected T? getItem(int position);
     method public int getItemCount();
-    method public void onLoadStateChanged(androidx.paging.LoadType type, androidx.paging.LoadState state);
-    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public final void retry();
   }
 
   public abstract class PagedListAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
     ctor protected PagedListAdapter(androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback);
     ctor protected PagedListAdapter(androidx.recyclerview.widget.AsyncDifferConfig<T> config);
-    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public androidx.paging.PagedList<T>? getCurrentList();
     method protected T? getItem(int position);
     method public int getItemCount();
     method @Deprecated public void onCurrentListChanged(androidx.paging.PagedList<T>? currentList);
     method public void onCurrentListChanged(androidx.paging.PagedList<T>? previousList, androidx.paging.PagedList<T>? currentList);
-    method public void onLoadStateChanged(androidx.paging.LoadType type, androidx.paging.LoadState state);
-    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public void submitList(androidx.paging.PagedList<T>? pagedList);
     method public void submitList(androidx.paging.PagedList<T>? pagedList, Runnable? commitCallback);
     property public androidx.paging.PagedList<T>? currentList;
diff --git a/paging/runtime/api/restricted_3.0.0-alpha01.txt b/paging/runtime/api/restricted_3.0.0-alpha01.txt
index 37e07ce..2e8c621 100644
--- a/paging/runtime/api/restricted_3.0.0-alpha01.txt
+++ b/paging/runtime/api/restricted_3.0.0-alpha01.txt
@@ -61,26 +61,24 @@
 
   public abstract class PagedDataAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
     ctor public PagedDataAdapter(androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback, kotlinx.coroutines.CoroutineDispatcher mainDispatcher);
-    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public final void connect(kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<T>> flow, kotlinx.coroutines.CoroutineScope scope);
     method protected T? getItem(int position);
     method public int getItemCount();
-    method public void onLoadStateChanged(androidx.paging.LoadType type, androidx.paging.LoadState state);
-    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public final void retry();
   }
 
   public abstract class PagedListAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
     ctor protected PagedListAdapter(androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback);
     ctor protected PagedListAdapter(androidx.recyclerview.widget.AsyncDifferConfig<T> config);
-    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public androidx.paging.PagedList<T>? getCurrentList();
     method protected T? getItem(int position);
     method public int getItemCount();
     method @Deprecated public void onCurrentListChanged(androidx.paging.PagedList<T>? currentList);
     method public void onCurrentListChanged(androidx.paging.PagedList<T>? previousList, androidx.paging.PagedList<T>? currentList);
-    method public void onLoadStateChanged(androidx.paging.LoadType type, androidx.paging.LoadState state);
-    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public void submitList(androidx.paging.PagedList<T>? pagedList);
     method public void submitList(androidx.paging.PagedList<T>? pagedList, Runnable? commitCallback);
     property public androidx.paging.PagedList<T>? currentList;
diff --git a/paging/runtime/api/restricted_current.txt b/paging/runtime/api/restricted_current.txt
index 37e07ce..2e8c621 100644
--- a/paging/runtime/api/restricted_current.txt
+++ b/paging/runtime/api/restricted_current.txt
@@ -61,26 +61,24 @@
 
   public abstract class PagedDataAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
     ctor public PagedDataAdapter(androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback, kotlinx.coroutines.CoroutineDispatcher mainDispatcher);
-    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public final void connect(kotlinx.coroutines.flow.Flow<androidx.paging.PagedData<T>> flow, kotlinx.coroutines.CoroutineScope scope);
     method protected T? getItem(int position);
     method public int getItemCount();
-    method public void onLoadStateChanged(androidx.paging.LoadType type, androidx.paging.LoadState state);
-    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public final void retry();
   }
 
   public abstract class PagedListAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
     ctor protected PagedListAdapter(androidx.recyclerview.widget.DiffUtil.ItemCallback<T> diffCallback);
     ctor protected PagedListAdapter(androidx.recyclerview.widget.AsyncDifferConfig<T> config);
-    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void addLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public androidx.paging.PagedList<T>? getCurrentList();
     method protected T? getItem(int position);
     method public int getItemCount();
     method @Deprecated public void onCurrentListChanged(androidx.paging.PagedList<T>? currentList);
     method public void onCurrentListChanged(androidx.paging.PagedList<T>? previousList, androidx.paging.PagedList<T>? currentList);
-    method public void onLoadStateChanged(androidx.paging.LoadType type, androidx.paging.LoadState state);
-    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> callback);
+    method public void removeLoadStateListener(kotlin.jvm.functions.Function2<? super androidx.paging.LoadType,? super androidx.paging.LoadState,kotlin.Unit> listener);
     method public void submitList(androidx.paging.PagedList<T>? pagedList);
     method public void submitList(androidx.paging.PagedList<T>? pagedList, Runnable? commitCallback);
     property public androidx.paging.PagedList<T>? currentList;
diff --git a/paging/runtime/src/main/java/androidx/paging/AsyncPagedDataDiffer.kt b/paging/runtime/src/main/java/androidx/paging/AsyncPagedDataDiffer.kt
index ef8d7be..40e68ee 100644
--- a/paging/runtime/src/main/java/androidx/paging/AsyncPagedDataDiffer.kt
+++ b/paging/runtime/src/main/java/androidx/paging/AsyncPagedDataDiffer.kt
@@ -35,9 +35,8 @@
     private val updateCallback: ListUpdateCallback
 ) {
     internal val callback = object : PresenterCallback {
-        override fun onInserted(position: Int, count: Int, loadStates: Map<LoadType, LoadState>?) {
+        override fun onInserted(position: Int, count: Int) {
             updateCallback.onInserted(position, count)
-            loadStates?.entries?.forEach { onStateUpdate(it.key, it.value) }
         }
 
         override fun onRemoved(position: Int, count: Int) =
@@ -77,18 +76,23 @@
     }
 
     private val differBase = object : PagedDataDiffer<T>(mainDispatcher, workerDispatcher) {
-        override suspend fun performDiff(previous: NullPaddedList<T>, new: NullPaddedList<T>) {
+        override suspend fun performDiff(
+            previousList: NullPaddedList<T>,
+            newList: NullPaddedList<T>,
+            newLoadStates: Map<LoadType, LoadState>
+        ) {
             withContext(mainDispatcher) {
                 when {
-                    previous.size == 0 -> // fast path for no items -> some items
-                        callback.onInserted(0, new.size, null)
-                    new.size == 0 -> // fast path for some items -> no items
-                        callback.onRemoved(0, previous.size)
+                    previousList.size == 0 -> // fast path for no items -> some items
+                        callback.onInserted(0, newList.size)
+                    newList.size == 0 -> // fast path for some items -> no items
+                        callback.onRemoved(0, previousList.size)
                     else -> { // full diff
                         val diffResult = withContext(workerDispatcher) {
-                            previous.computeDiff(new, diffCallback)
+                            previousList.computeDiff(newList, diffCallback)
                         }
-                        previous.dispatchDiff(updateCallback, new, diffResult)
+                        previousList.dispatchDiff(updateCallback, newList, diffResult)
+                        newLoadStates.entries.forEach { callback.onStateUpdate(it.key, it.value) }
                     }
                 }
             }
@@ -134,8 +138,8 @@
     /**
      * Add a listener to observe the loading state.
      *
-     * As new PagedLists are submitted and displayed, the listener will be notified to reflect
-     * current REFRESH, START, and END states.
+     * As new PagedData generations are submitted and displayed, the listener will be notified to
+     * reflect current [LoadType.REFRESH], [LoadType.START], and [LoadType.END] states.
      *
      * @param listener [LoadStateListener] to receive updates.
      *
@@ -155,7 +159,6 @@
      * Remove a previously registered load state listener.
      *
      * @param listener Previously registered listener.
-     *
      * @see addLoadStateListener
      */
     open fun removeLoadStateListener(listener: (LoadType, LoadState) -> Unit) {
diff --git a/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.kt b/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.kt
index 055874f..afac8b5 100644
--- a/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.kt
+++ b/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.kt
@@ -472,16 +472,16 @@
     }
 
     /**
-     * Add a [LoadStateListener] to observe the loading state of the current [PagedList].
+     * Add a [LoadState] listener to observe the loading state of the current [PagedList].
      *
      * As new PagedLists are submitted and displayed, the listener will be notified to reflect
-     * current REFRESH, START, and END states.
+     * current [LoadType.REFRESH], [LoadType.START], and [LoadType.END] states.
      *
-     * @param listener [LoadStateListener] to receive updates.
+     * @param listener [LoadState] listener to receive updates.
      *
      * @see removeLoadStateListener
      */
-    open fun addLoadStateListener(listener: LoadStateListener) {
+    open fun addLoadStateListener(listener: (type: LoadType, state: LoadState) -> Unit) {
         val pagedList = this.pagedList
         if (pagedList != null) {
             pagedList.addWeakLoadStateListener(listener)
@@ -492,14 +492,14 @@
     }
 
     /**
-     * Remove a previously registered [LoadStateListener].
+     * Remove a previously registered [LoadState] listener.
      *
      * @param listener Previously registered listener.
      *
      * @see currentList
      * @see addPagedListListener
      */
-    open fun removeLoadStateListener(listener: LoadStateListener) {
+    open fun removeLoadStateListener(listener: (type: LoadType, state: LoadState) -> Unit) {
         loadStateListeners.remove(listener)
         pagedList?.removeWeakLoadStateListener(listener)
     }
diff --git a/paging/runtime/src/main/java/androidx/paging/PagedDataAdapter.kt b/paging/runtime/src/main/java/androidx/paging/PagedDataAdapter.kt
index 285319e..0c5a1a9 100644
--- a/paging/runtime/src/main/java/androidx/paging/PagedDataAdapter.kt
+++ b/paging/runtime/src/main/java/androidx/paging/PagedDataAdapter.kt
@@ -32,11 +32,7 @@
         mainDispatcher = mainDispatcher,
         diffCallback = diffCallback,
         updateCallback = AdapterListUpdateCallback(this)
-    ).also {
-        it.addLoadStateListener { loadType, loadState ->
-            onLoadStateChanged(loadType, loadState)
-        }
-    }
+    )
 
     fun connect(flow: Flow<PagedData<T>>, scope: CoroutineScope) {
         differ.connect(flow, scope)
@@ -51,39 +47,26 @@
     override fun getItemCount() = differ.itemCount
 
     /**
-     * Called when the [LoadState] for a particular type of load (START, END, REFRESH) has
-     * changed.
+     * Add a [LoadState] listener to observe the loading state of the current [PagedData].
      *
-     * REFRESH events can be used to drive a `SwipeRefreshLayout`, or START/END events
-     * can be used to drive loading spinner items in the Adapter.
+     * As new PagedData generations are submitted and displayed, the listener will be notified to
+     * reflect current [LoadType.REFRESH], [LoadType.START], and [LoadType.END] states.
      *
-     * @param type [LoadType] Can be START, END, or REFRESH
-     * @param state [LoadState] Idle, Loading, Done, or Error.
-     */
-    open fun onLoadStateChanged(type: LoadType, state: LoadState) {
-    }
-
-    /**
-     * Add a [LoadStateListener] to observe the loading state of the current [PagedList].
-     *
-     * As new PagedLists are submitted and displayed, the callback will be notified to reflect
-     * current REFRESH, START, and END states.
-     *
-     * @param callback [LoadStateListener] to receive updates.
+     * @param listener [LoadState] listener to receive updates.
      *
      * @see removeLoadStateListener
      */
-    open fun addLoadStateListener(callback: (LoadType, LoadState) -> Unit) {
-        differ.addLoadStateListener(callback)
+    open fun addLoadStateListener(listener: (LoadType, LoadState) -> Unit) {
+        differ.addLoadStateListener(listener)
     }
 
     /**
-     * Remove a previously registered [LoadStateListener].
+     * Remove a previously registered [LoadState] listener.
      *
-     * @param callback Previously registered callback.
+     * @param listener Previously registered listener.
      * @see addLoadStateListener
      */
-    open fun removeLoadStateListener(callback: (LoadType, LoadState) -> Unit) {
-        differ.removeLoadStateListener(callback)
+    open fun removeLoadStateListener(listener: (LoadType, LoadState) -> Unit) {
+        differ.removeLoadStateListener(listener)
     }
 }
diff --git a/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.kt b/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.kt
index b1aaa50..a8b1eabe 100644
--- a/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.kt
+++ b/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.kt
@@ -111,8 +111,6 @@
         this@PagedListAdapter.onCurrentListChanged(currentList)
         this@PagedListAdapter.onCurrentListChanged(previousList, currentList)
     }
-    private val loadStateListener
-        get() = this::onLoadStateChanged
 
     /**
      * Returns the [PagedList] currently being displayed by the [PagedListAdapter].
@@ -140,13 +138,11 @@
     protected constructor(diffCallback: DiffUtil.ItemCallback<T>) {
         differ = AsyncPagedListDiffer(this, diffCallback)
         differ.addPagedListListener(listener)
-        differ.addLoadStateListener(loadStateListener)
     }
 
     protected constructor(config: AsyncDifferConfig<T>) {
         differ = AsyncPagedListDiffer(AdapterListUpdateCallback(this), config)
         differ.addPagedListListener(listener)
-        differ.addLoadStateListener(loadStateListener)
     }
 
     /**
@@ -224,39 +220,26 @@
     }
 
     /**
-     * Called when the [LoadState] for a particular type of load (START, END, REFRESH) has
-     * changed.
+     * Add a [LoadState] listener to observe the loading state of the current [PagedList].
      *
-     * REFRESH events can be used to drive a `SwipeRefreshLayout`, or START/END events
-     * can be used to drive loading spinner items in the Adapter.
+     * As new PagedLists are submitted and displayed, the listener will be notified to reflect
+     * current [LoadType.REFRESH], [LoadType.START], and [LoadType.END] states.
      *
-     * @param type [LoadType] Can be START, END, or REFRESH
-     * @param state [LoadState] Idle, Loading, Done, or Error.
-     */
-    open fun onLoadStateChanged(type: LoadType, state: LoadState) {
-    }
-
-    /**
-     * Add a [LoadStateListener] to observe the loading state of the current [PagedList].
-     *
-     * As new PagedLists are submitted and displayed, the callback will be notified to reflect
-     * current REFRESH, START, and END states.
-     *
-     * @param callback [LoadStateListener] to receive updates.
+     * @param listener [LoadStateListener] to receive updates.
      *
      * @see removeLoadStateListener
      */
-    open fun addLoadStateListener(callback: LoadStateListener) {
-        differ.addLoadStateListener(callback)
+    open fun addLoadStateListener(listener: (type: LoadType, state: LoadState) -> Unit) {
+        differ.addLoadStateListener(listener)
     }
 
     /**
-     * Remove a previously registered [LoadStateListener].
+     * Remove a previously registered [LoadState] listener.
      *
-     * @param callback Previously registered callback.
+     * @param listener Previously registered listener.
      * @see addLoadStateListener
      */
-    open fun removeLoadStateListener(callback: LoadStateListener) {
-        differ.removeLoadStateListener(callback)
+    open fun removeLoadStateListener(listener: (type: LoadType, state: LoadState) -> Unit) {
+        differ.removeLoadStateListener(listener)
     }
 }
diff --git a/paging/rxjava2/api/3.0.0-alpha01.txt b/paging/rxjava2/api/3.0.0-alpha01.txt
index c963f93..0059dfe 100644
--- a/paging/rxjava2/api/3.0.0-alpha01.txt
+++ b/paging/rxjava2/api/3.0.0-alpha01.txt
@@ -2,7 +2,7 @@
 package androidx.paging {
 
   public final class RxPagedDataFlowBuilder<Key, Value> {
-    ctor public RxPagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public RxPagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagingConfig config);
     method public io.reactivex.Observable<androidx.paging.PagedData<Value>> build();
     method public androidx.paging.RxPagedDataFlowBuilder<Key,Value> setInitialKey(Key? initialKey);
   }
diff --git a/paging/rxjava2/api/current.txt b/paging/rxjava2/api/current.txt
index c963f93..0059dfe 100644
--- a/paging/rxjava2/api/current.txt
+++ b/paging/rxjava2/api/current.txt
@@ -2,7 +2,7 @@
 package androidx.paging {
 
   public final class RxPagedDataFlowBuilder<Key, Value> {
-    ctor public RxPagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public RxPagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagingConfig config);
     method public io.reactivex.Observable<androidx.paging.PagedData<Value>> build();
     method public androidx.paging.RxPagedDataFlowBuilder<Key,Value> setInitialKey(Key? initialKey);
   }
diff --git a/paging/rxjava2/api/public_plus_experimental_3.0.0-alpha01.txt b/paging/rxjava2/api/public_plus_experimental_3.0.0-alpha01.txt
index c963f93..0059dfe 100644
--- a/paging/rxjava2/api/public_plus_experimental_3.0.0-alpha01.txt
+++ b/paging/rxjava2/api/public_plus_experimental_3.0.0-alpha01.txt
@@ -2,7 +2,7 @@
 package androidx.paging {
 
   public final class RxPagedDataFlowBuilder<Key, Value> {
-    ctor public RxPagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public RxPagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagingConfig config);
     method public io.reactivex.Observable<androidx.paging.PagedData<Value>> build();
     method public androidx.paging.RxPagedDataFlowBuilder<Key,Value> setInitialKey(Key? initialKey);
   }
diff --git a/paging/rxjava2/api/public_plus_experimental_current.txt b/paging/rxjava2/api/public_plus_experimental_current.txt
index c963f93..0059dfe 100644
--- a/paging/rxjava2/api/public_plus_experimental_current.txt
+++ b/paging/rxjava2/api/public_plus_experimental_current.txt
@@ -2,7 +2,7 @@
 package androidx.paging {
 
   public final class RxPagedDataFlowBuilder<Key, Value> {
-    ctor public RxPagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public RxPagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagingConfig config);
     method public io.reactivex.Observable<androidx.paging.PagedData<Value>> build();
     method public androidx.paging.RxPagedDataFlowBuilder<Key,Value> setInitialKey(Key? initialKey);
   }
diff --git a/paging/rxjava2/api/restricted_3.0.0-alpha01.txt b/paging/rxjava2/api/restricted_3.0.0-alpha01.txt
index c963f93..0059dfe 100644
--- a/paging/rxjava2/api/restricted_3.0.0-alpha01.txt
+++ b/paging/rxjava2/api/restricted_3.0.0-alpha01.txt
@@ -2,7 +2,7 @@
 package androidx.paging {
 
   public final class RxPagedDataFlowBuilder<Key, Value> {
-    ctor public RxPagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public RxPagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagingConfig config);
     method public io.reactivex.Observable<androidx.paging.PagedData<Value>> build();
     method public androidx.paging.RxPagedDataFlowBuilder<Key,Value> setInitialKey(Key? initialKey);
   }
diff --git a/paging/rxjava2/api/restricted_current.txt b/paging/rxjava2/api/restricted_current.txt
index c963f93..0059dfe 100644
--- a/paging/rxjava2/api/restricted_current.txt
+++ b/paging/rxjava2/api/restricted_current.txt
@@ -2,7 +2,7 @@
 package androidx.paging {
 
   public final class RxPagedDataFlowBuilder<Key, Value> {
-    ctor public RxPagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public RxPagedDataFlowBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagingConfig config);
     method public io.reactivex.Observable<androidx.paging.PagedData<Value>> build();
     method public androidx.paging.RxPagedDataFlowBuilder<Key,Value> setInitialKey(Key? initialKey);
   }
diff --git a/paging/rxjava2/src/main/java/androidx/paging/RxPagedDataFlowBuilder.kt b/paging/rxjava2/src/main/java/androidx/paging/RxPagedDataFlowBuilder.kt
index 73a0f8e..9050696 100644
--- a/paging/rxjava2/src/main/java/androidx/paging/RxPagedDataFlowBuilder.kt
+++ b/paging/rxjava2/src/main/java/androidx/paging/RxPagedDataFlowBuilder.kt
@@ -30,7 +30,7 @@
  */
 class RxPagedDataFlowBuilder<Key : Any, Value : Any>(
     pagedSourceFactory: () -> PagedSource<Key, Value>,
-    config: PagedList.Config
+    config: PagingConfig
 ) {
     private val baseBuilder = PagedDataFlowBuilder(pagedSourceFactory, config)
 
diff --git a/paging/testutils/build.gradle b/paging/testutils/build.gradle
new file mode 100644
index 0000000..1bc3e5b
--- /dev/null
+++ b/paging/testutils/build.gradle
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+
+import androidx.build.AndroidXExtension
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("kotlin")
+}
+
+dependencies {
+    api(KOTLIN_STDLIB)
+    implementation(project(":paging:paging-common"))
+    implementation(KOTLIN_TEST)
+}
+
+androidx {
+    name = "Android Paging Internal Test Utils"
+    publish = Publish.SNAPSHOT_AND_RELEASE
+    mavenVersion = LibraryVersions.PAGING
+    mavenGroup = LibraryGroups.PAGING
+    inceptionYear = "2020"
+    description = "Android Paging Internal Test Utils"
+    url = AndroidXExtension.ARCHITECTURE_URL
+}
diff --git a/paging/testutils/src/main/java/androidx/paging/PageEventUtils.kt b/paging/testutils/src/main/java/androidx/paging/PageEventUtils.kt
new file mode 100644
index 0000000..006e366
--- /dev/null
+++ b/paging/testutils/src/main/java/androidx/paging/PageEventUtils.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.paging
+
+import kotlin.test.assertEquals
+
+fun <T> assertEvents(expected: List<T>, actual: List<T>) {
+    try {
+        assertEquals(expected, actual)
+    } catch (e: Throwable) {
+        val msg = e.localizedMessage
+            .replace("),", "),\n")
+            .replace("<[", "<[\n ")
+            .replace("actual", "\nactual")
+            .lines()
+            .toMutableList()
+
+        if (expected.count() != actual.count()) throw AssertionError(msg.joinToString("\n"))
+
+        var index = 0
+        for (i in 0 until expected.count()) {
+            if (expected[i] != actual[i]) {
+                index = i
+                break
+            }
+        }
+        msg[index + 1] = msg[index + 1].prependIndent(" >")
+        msg[msg.count() / 2 + index + 1] = msg[msg.count() / 2 + index + 1].prependIndent(" >")
+        throw AssertionError(msg.joinToString("\n"))
+    }
+}
diff --git a/paging/common/src/test/kotlin/androidx/paging/TestPagedSource.kt b/paging/testutils/src/main/java/androidx/paging/TestPagedSource.kt
similarity index 97%
rename from paging/common/src/test/kotlin/androidx/paging/TestPagedSource.kt
rename to paging/testutils/src/main/java/androidx/paging/TestPagedSource.kt
index bae6e86..5a2027a 100644
--- a/paging/common/src/test/kotlin/androidx/paging/TestPagedSource.kt
+++ b/paging/testutils/src/main/java/androidx/paging/TestPagedSource.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -69,4 +69,4 @@
         val items = List(100) { it }
         val LOAD_ERROR = Exception("Exception from TestPagedSource.errorNextLoad")
     }
-}
\ No newline at end of file
+}
diff --git a/percentlayout/percentlayout/src/main/java/androidx/percentlayout/widget/PercentRelativeLayout.java b/percentlayout/percentlayout/src/main/java/androidx/percentlayout/widget/PercentRelativeLayout.java
index 32719c2..65bd5d7 100644
--- a/percentlayout/percentlayout/src/main/java/androidx/percentlayout/widget/PercentRelativeLayout.java
+++ b/percentlayout/percentlayout/src/main/java/androidx/percentlayout/widget/PercentRelativeLayout.java
@@ -125,6 +125,7 @@
  *         app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" /&gt
  *
  * &lt;/androidx.constraintlayout.widget.ConstraintLayout&gt
+ * </pre>
  */
 @SuppressWarnings("deprecation")
 @Deprecated
diff --git a/preference/preference/src/main/java/androidx/preference/DropDownPreference.java b/preference/preference/src/main/java/androidx/preference/DropDownPreference.java
index 45d0357..d66dc34 100644
--- a/preference/preference/src/main/java/androidx/preference/DropDownPreference.java
+++ b/preference/preference/src/main/java/androidx/preference/DropDownPreference.java
@@ -17,6 +17,7 @@
 package androidx.preference;
 
 import android.content.Context;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.AdapterView;
@@ -136,7 +137,7 @@
         CharSequence[] entryValues = getEntryValues();
         if (value != null && entryValues != null) {
             for (int i = entryValues.length - 1; i >= 0; i--) {
-                if (entryValues[i].equals(value)) {
+                if (TextUtils.equals(entryValues[i].toString(), value)) {
                     return i;
                 }
             }
diff --git a/preference/preference/src/main/java/androidx/preference/ListPreference.java b/preference/preference/src/main/java/androidx/preference/ListPreference.java
index 5b67a8b..3f1361d 100644
--- a/preference/preference/src/main/java/androidx/preference/ListPreference.java
+++ b/preference/preference/src/main/java/androidx/preference/ListPreference.java
@@ -148,9 +148,9 @@
     @Override
     public void setSummary(CharSequence summary) {
         super.setSummary(summary);
-        if (summary == null && mSummary != null) {
+        if (summary == null) {
             mSummary = null;
-        } else if (summary != null && !summary.equals(mSummary)) {
+        } else {
             mSummary = summary.toString();
         }
     }
@@ -222,7 +222,7 @@
     public int findIndexOfValue(String value) {
         if (value != null && mEntryValues != null) {
             for (int i = mEntryValues.length - 1; i >= 0; i--) {
-                if (mEntryValues[i].equals(value)) {
+                if (TextUtils.equals(mEntryValues[i].toString(), value)) {
                     return i;
                 }
             }
diff --git a/preference/preference/src/main/java/androidx/preference/MultiSelectListPreference.java b/preference/preference/src/main/java/androidx/preference/MultiSelectListPreference.java
index 931c9b9..008d326 100644
--- a/preference/preference/src/main/java/androidx/preference/MultiSelectListPreference.java
+++ b/preference/preference/src/main/java/androidx/preference/MultiSelectListPreference.java
@@ -20,6 +20,7 @@
 import android.content.res.TypedArray;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 
 import androidx.annotation.ArrayRes;
@@ -166,7 +167,7 @@
     public int findIndexOfValue(String value) {
         if (value != null && mEntryValues != null) {
             for (int i = mEntryValues.length - 1; i >= 0; i--) {
-                if (mEntryValues[i].equals(value)) {
+                if (TextUtils.equals(mEntryValues[i].toString(), value)) {
                     return i;
                 }
             }
diff --git a/preference/preference/src/main/java/androidx/preference/Preference.java b/preference/preference/src/main/java/androidx/preference/Preference.java
index fd765a3..9d9932c 100644
--- a/preference/preference/src/main/java/androidx/preference/Preference.java
+++ b/preference/preference/src/main/java/androidx/preference/Preference.java
@@ -654,7 +654,7 @@
      * @param title The title for this preference
      */
     public void setTitle(CharSequence title) {
-        if ((title == null && mTitle != null) || (title != null && !title.equals(mTitle))) {
+        if (!TextUtils.equals(title, mTitle)) {
             mTitle = title;
             notifyChanged();
         }
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerSavedStateTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerSavedStateTest.java
index ac1562c..78bf4da 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerSavedStateTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerSavedStateTest.java
@@ -270,8 +270,8 @@
 
     // TODO(b/142682722): This test is currently causing process crashes on CuttleFish API 29
     //  devices that are currently assumed to not be an issue with RecyclerView or this test.
-    //  Therefore we are temporarily supressing the test on all sdk versions 29 and above until
-    //  the issue is resovled so that RecyclerView CLs are not blocked.
+    //  Therefore we are temporarily suppressing the test on all sdk versions 29 and above until
+    //  the issue is resolved so that RecyclerView CLs are not blocked.
     @Test
     @SdkSuppress(maxSdkVersion = 28)
     public void savedStateTest()
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerSnappingTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerSnappingTest.java
index da14e85..f602340 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerSnappingTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerSnappingTest.java
@@ -245,7 +245,8 @@
 
         mLayoutManager.expectIdleState(2);
         smoothScrollBy(scrollDist);
-        mLayoutManager.waitForSnap(25);
+        // Very high number to try to reduce flakiness.
+        mLayoutManager.waitForSnap(50);
     }
 
     private int getViewDimension(View view) {
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt b/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
index d5b85f4..84441fe 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
@@ -43,8 +43,8 @@
     val MISSING_RAWQUERY_ANNOTATION = "RawQuery methods must be annotated with" +
             " ${RawQuery::class.java}"
     val INVALID_ON_CONFLICT_VALUE = "On conflict value must be one of @OnConflictStrategy values."
-    val TRANSACTION_REFERENCE_DOCS = "https://developer.android.com/reference/android/arch/" +
-            "persistence/room/Transaction.html"
+    val TRANSACTION_REFERENCE_DOCS = "https://developer.android.com/reference/androidx/" +
+            "room/Transaction.html"
 
     val ABSTRACT_METHOD_IN_DAO_MISSING_ANY_ANNOTATION = "Abstract method in DAO must be annotated" +
             " with ${Query::class.java} AND ${Insert::class.java}"
diff --git a/room/compiler/src/main/kotlin/androidx/room/verifier/DatabaseVerifier.kt b/room/compiler/src/main/kotlin/androidx/room/verifier/DatabaseVerifier.kt
index a7e87c6..0a38780 100644
--- a/room/compiler/src/main/kotlin/androidx/room/verifier/DatabaseVerifier.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/verifier/DatabaseVerifier.kt
@@ -62,9 +62,16 @@
          */
         private val SQLITE_NATIVE_LIB_EXTENSIONS = arrayOf(".so", ".jnilib", ".dll")
 
-        private val sqliteNativeLibDir: File
+        private lateinit var sqliteNativeLibDir: File
 
         init {
+            copyNativeLibs()
+        }
+
+        /**
+         * Copies native libraries into a tmp folder to be loaded.
+         */
+        private fun copyNativeLibs() {
             // see: https://github.com/xerial/sqlite-jdbc/issues/97
             val tmpDir = System.getProperty("java.io.tmpdir")
             checkNotNull(tmpDir) {
@@ -106,7 +113,14 @@
                         nativeLibs.forEach {
                             it.setExecutable(true)
                             context.logger.d("reloading the sqlite native file: $it")
-                            System.load(it.absoluteFile.absolutePath)
+                            try {
+                                System.load(it.absoluteFile.absolutePath)
+                            } catch (unsatisfied: UnsatisfiedLinkError) {
+                                // https://issuetracker.google.com/issues/146061836
+                                // workaround for b/146061836 where we just copy it again as
+                                // another file.
+                                copyNativeLibs()
+                            }
                         }
                     } else {
                         context.logger.w(Warning.CANNOT_CREATE_VERIFICATION_DATABASE, element,
diff --git a/room/integration-tests/incremental-annotation-processing/build.gradle b/room/integration-tests/incremental-annotation-processing/build.gradle
index 6139835..acc978a 100644
--- a/room/integration-tests/incremental-annotation-processing/build.gradle
+++ b/room/integration-tests/incremental-annotation-processing/build.gradle
@@ -25,6 +25,8 @@
 
 dependencies {
     implementation(KOTLIN_STDLIB)
+
+    testImplementation(project(":internal-testutils-gradle-plugin"))
     testImplementation(JUNIT)
     testImplementation(TRUTH)
     testImplementation gradleTestKit()
diff --git a/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt b/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt
index d9fd1da..1bf80e6 100644
--- a/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt
+++ b/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.gradle
 
+import androidx.testutils.gradle.ProjectSetupRule
 import com.google.common.truth.Expect
 import org.gradle.testkit.runner.BuildResult
 import org.gradle.testkit.runner.GradleRunner
@@ -23,12 +24,10 @@
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
-import org.junit.rules.TemporaryFolder
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 import java.io.File
 import java.nio.file.Files
-import java.util.Properties
 
 @RunWith(Parameterized::class)
 class RoomIncrementalAnnotationProcessingTest(private val withIncrementalRoom: Boolean) {
@@ -49,20 +48,11 @@
     }
 
     @get:Rule
-    val testProjectDir = TemporaryFolder()
+    val projectSetup = ProjectSetupRule()
 
     @get:Rule
     val expect: Expect = Expect.create()
 
-    // Properties to set up test project
-    private lateinit var prebuiltsRoot: String
-    private lateinit var agpDependency: String
-    private lateinit var localSupportRepo: String
-    private lateinit var compileSdkVersion: String
-    private lateinit var buildToolsVersion: String
-    private lateinit var minSdkVersion: String
-    private lateinit var debugKeystore: String
-
     // Original source files
     private lateinit var srcDatabase1: File
     private lateinit var srcDao1: File
@@ -100,34 +90,14 @@
 
     @Before
     fun setup() {
-        val projectRoot = testProjectDir.root
-
-        // copy local.properties
-        File("../../../local.properties")
-            .copyTo(File(projectRoot, "local.properties"), overwrite = true)
-
-        // copy sdk.prop (created by module's build.gradle)
-        RoomIncrementalAnnotationProcessingTest::class.java.classLoader
-            .getResourceAsStream("sdk.prop")
-            .use { input ->
-                val properties = Properties().apply { load(input) }
-                prebuiltsRoot = properties.getProperty("prebuiltsRoot")
-                localSupportRepo = properties.getProperty("localSupportRepo")
-                agpDependency = properties.getProperty("agpDependency")
-                compileSdkVersion = properties.getProperty("compileSdkVersion")
-                buildToolsVersion = properties.getProperty("buildToolsVersion")
-                minSdkVersion = properties.getProperty("minSdkVersion")
-                debugKeystore = properties.getProperty("debugKeystore")
-            }
+        val projectRoot = projectSetup.rootDir
+        val prebuiltsRoot = projectSetup.props.prebuiltsRoot
+        val localSupportRepo = projectSetup.props.localSupportRepo
+        val agpDependency = projectSetup.props.agpDependency
 
         // copy test project
         File("src/test/data/simple-project").copyRecursively(projectRoot)
 
-        // setup gradle.properties
-        File(projectRoot, "gradle.properties").writeText(
-            "android.useAndroidX=true"
-        )
-
         // set up build file
         File(projectRoot, "build.gradle").writeText(
             """
@@ -154,20 +124,7 @@
                 }
             }
 
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
-                }
-
-                signingConfigs {
-                    debug {
-                        storeFile file("$debugKeystore")
-                    }
-                }
-            }
+            %s
 
             dependencies {
                 // Uses latest Room built from tip of tree
@@ -200,7 +157,11 @@
                     }
                 }
             }
-        """.trimIndent()
+        """
+                .trimIndent()
+                // doing format instead of "$projectSetup.androidProject" on purpose,
+                // because otherwise trimIndent will mess with formatting
+                .format(projectSetup.androidProject)
         )
 
         // Compute file paths
@@ -230,7 +191,7 @@
 
     private fun runGradleTasks(vararg args: String): BuildResult {
         return GradleRunner.create()
-            .withProjectDir(testProjectDir.root)
+            .withProjectDir(projectSetup.rootDir)
             .withArguments(*args)
             .build()
     }
diff --git a/room/runtime/src/main/java/androidx/room/InvalidationTracker.java b/room/runtime/src/main/java/androidx/room/InvalidationTracker.java
index f35b7f9..c7ea442 100644
--- a/room/runtime/src/main/java/androidx/room/InvalidationTracker.java
+++ b/room/runtime/src/main/java/androidx/room/InvalidationTracker.java
@@ -361,8 +361,8 @@
         public void run() {
             final Lock closeLock = mDatabase.getCloseLock();
             Set<Integer> invalidatedTableIds = null;
+            closeLock.lock();
             try {
-                closeLock.lock();
 
                 if (!ensureInitialization()) {
                     return;
diff --git a/room/runtime/src/main/java/androidx/room/RoomDatabase.java b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
index b038c89..8595be4 100644
--- a/room/runtime/src/main/java/androidx/room/RoomDatabase.java
+++ b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
@@ -241,8 +241,8 @@
     public void close() {
         if (isOpen()) {
             final Lock closeLock = mCloseLock.writeLock();
+            closeLock.lock();
             try {
-                closeLock.lock();
                 mInvalidationTracker.stopMultiInstanceInvalidation();
                 mOpenHelper.close();
             } finally {
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/ContentUriKeyProvider.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/ContentUriKeyProvider.java
deleted file mode 100644
index cc83ca1..0000000
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/ContentUriKeyProvider.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.supportv7.widget.selection.fancy;
-
-import android.net.Uri;
-
-import androidx.annotation.Nullable;
-import androidx.recyclerview.selection.ItemKeyProvider;
-
-import java.util.HashMap;
-import java.util.Map;
-
-class ContentUriKeyProvider extends ItemKeyProvider<Uri> {
-
-    private final Uri[] mUris;
-    private final Map<Uri, Integer> mPositions;
-
-    ContentUriKeyProvider(String authority, String[] values) {
-        // Advise the world we can supply ids/position for entire copus
-        // at any time.
-        super(SCOPE_MAPPED);
-
-        mUris = new Uri[values.length];
-        mPositions = new HashMap<>();
-
-        for (int i = 0; i < values.length; i++) {
-            mUris[i] = new Uri.Builder()
-                    .scheme("content")
-                    .encodedAuthority(authority)
-                    .appendPath(values[i])
-                    .build();
-            mPositions.put(mUris[i], i);
-        }
-    }
-
-    @Override
-    public @Nullable Uri getKey(int position) {
-        return mUris[position];
-    }
-
-    @Override
-    public int getPosition(Uri key) {
-        return mPositions.get(key);
-    }
-}
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyDetailsLookup.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyDetailsLookup.java
index 92ed977..c2d57f0 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyDetailsLookup.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyDetailsLookup.java
@@ -41,9 +41,11 @@
         @Nullable View view = mRecView.findChildViewUnder(e.getX(), e.getY());
         if (view != null) {
             ViewHolder holder = mRecView.getChildViewHolder(view);
-            if (holder instanceof FancyHolder) {
-                return ((FancyHolder) holder).getItemDetails();
+            if (holder instanceof FancyItemHolder) {
+                return ((FancyItemHolder) holder).getItemDetails();
             }
+            // FancyHeaderHolder doesn't hold a selectable item,
+            // so it doesn't support getItemDetails.
         }
         return null;
     }
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyHeaderHolder.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyHeaderHolder.java
new file mode 100644
index 0000000..4ed2d1f
--- /dev/null
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyHeaderHolder.java
@@ -0,0 +1,47 @@
+/*
+ * 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 com.example.android.supportv7.widget.selection.fancy;
+
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.example.android.supportv7.R;
+
+final class FancyHeaderHolder extends FancyHolder {
+
+    private static final String HEADER_TAG = "I'm a header";
+    public final TextView mLabel;
+
+    FancyHeaderHolder(LinearLayout layout) {
+        super(layout);
+        layout.setTag(HEADER_TAG);
+        mLabel = layout.findViewById(R.id.label);
+    }
+
+    void update(String label) {
+        mLabel.setText(label.toUpperCase() + label + label + "...");
+    }
+
+    @Override
+    public String toString() {
+        return "Header{name:" + mLabel.getText() + "}";
+    }
+
+    static boolean isHeader(View view) {
+        return HEADER_TAG.equals(view.getTag());
+    }
+}
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyHolder.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyHolder.java
index 6637d88..6fbc206 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyHolder.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyHolder.java
@@ -13,111 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.example.android.supportv7.widget.selection.fancy;
 
-import android.graphics.Rect;
-import android.net.Uri;
-import android.view.MotionEvent;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.recyclerview.selection.ItemDetailsLookup.ItemDetails;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.example.android.supportv7.R;
-
-final class FancyHolder extends RecyclerView.ViewHolder {
-
-    private final LinearLayout mContainer;
-    public final TextView mSelector;
-    public final TextView mLabel;
-    private final ItemDetails<Uri> mDetails;
-
-    private @Nullable Uri mKey;
-
+abstract class FancyHolder extends RecyclerView.ViewHolder {
     FancyHolder(LinearLayout layout) {
         super(layout);
-        mContainer = layout.findViewById(R.id.container);
-        mSelector = layout.findViewById(R.id.selector);
-        mLabel = layout.findViewById(R.id.label);
-        mDetails = new ItemDetails<Uri>() {
-            @Override
-            public int getPosition() {
-                return FancyHolder.this.getAdapterPosition();
-            }
-
-            @Override
-            public Uri getSelectionKey() {
-                return FancyHolder.this.mKey;
-            }
-
-            @Override
-            public boolean inDragRegion(@NonNull MotionEvent e) {
-                return FancyHolder.this.inDragRegion(e);
-            }
-
-            @Override
-            public boolean inSelectionHotspot(@NonNull MotionEvent e) {
-                return FancyHolder.this.inSelectRegion(e);
-            }
-
-            @NonNull
-            @Override
-            public String toString() {
-                return FancyHolder.this.toString();
-            }
-        };
-    }
-
-    void update(Uri key, String label, boolean selected) {
-        mKey = key;
-        mLabel.setText(label);
-        setSelected(selected);
-    }
-
-    private void setSelected(boolean selected) {
-        mContainer.setActivated(selected);
-        mSelector.setActivated(selected);
-    }
-
-    boolean inDragRegion(MotionEvent event) {
-        // If itemView is activated = selected, then whole region is interactive
-        if (itemView.isActivated()) {
-            return true;
-        }
-
-        // Do everything in global coordinates - it makes things simpler.
-        int[] coords = new int[2];
-        mSelector.getLocationOnScreen(coords);
-
-        Rect textBounds = new Rect();
-        mLabel.getPaint().getTextBounds(
-                mLabel.getText().toString(), 0, mLabel.getText().length(), textBounds);
-
-        Rect rect = new Rect(
-                coords[0],
-                coords[1],
-                coords[0] + mSelector.getWidth() + textBounds.width(),
-                coords[1] + Math.max(mSelector.getHeight(), textBounds.height()));
-
-        // If the tap occurred inside icon or the text, these are interactive spots.
-        return rect.contains((int) event.getRawX(), (int) event.getRawY());
-    }
-
-    boolean inSelectRegion(MotionEvent e) {
-        Rect iconRect = new Rect();
-        mSelector.getGlobalVisibleRect(iconRect);
-        return iconRect.contains((int) e.getRawX(), (int) e.getRawY());
-    }
-
-    ItemDetails<Uri> getItemDetails() {
-        return mDetails;
-    }
-
-    @Override
-    public String toString() {
-        return "Item{name:" + mLabel.getText() + ", url:" + mKey + "}";
     }
 }
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyItemHolder.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyItemHolder.java
new file mode 100644
index 0000000..c7761b2
--- /dev/null
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancyItemHolder.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.supportv7.widget.selection.fancy;
+
+import android.graphics.Rect;
+import android.net.Uri;
+import android.view.MotionEvent;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.selection.ItemDetailsLookup.ItemDetails;
+
+import com.example.android.supportv7.R;
+
+final class FancyItemHolder extends FancyHolder {
+
+    private final LinearLayout mContainer;
+    private final TextView mSelector;
+    private final TextView mLabel;
+    private final ItemDetails<Uri> mDetails;
+
+    private @Nullable Uri mKey;
+
+    FancyItemHolder(LinearLayout layout) {
+        super(layout);
+
+        mContainer = layout.findViewById(R.id.container);
+        mSelector = layout.findViewById(R.id.selector);
+        mLabel = layout.findViewById(R.id.label);
+        mDetails = new ItemDetails<Uri>() {
+            @Override
+            public int getPosition() {
+                return FancyItemHolder.this.getAdapterPosition();
+            }
+
+            @Override
+            public Uri getSelectionKey() {
+                return FancyItemHolder.this.mKey;
+            }
+
+            @Override
+            public boolean inDragRegion(@NonNull MotionEvent e) {
+                return FancyItemHolder.this.inDragRegion(e);
+            }
+
+            @Override
+            public boolean inSelectionHotspot(@NonNull MotionEvent e) {
+                return FancyItemHolder.this.inSelectRegion(e);
+            }
+
+            @NonNull
+            @Override
+            public String toString() {
+                return FancyItemHolder.this.toString();
+            }
+        };
+    }
+
+    void update(Uri key, String label, boolean selected) {
+        mKey = key;
+        mLabel.setText(label);
+        setSelected(selected);
+    }
+
+    private void setSelected(boolean selected) {
+        mContainer.setActivated(selected);
+        mSelector.setActivated(selected);
+    }
+
+    boolean inDragRegion(MotionEvent event) {
+        // If itemView is activated = selected, then whole region is interactive
+        if (itemView.isActivated()) {
+            return true;
+        }
+
+        // Do everything in global coordinates - it makes things simpler.
+        int[] coords = new int[2];
+        mSelector.getLocationOnScreen(coords);
+
+        Rect textBounds = new Rect();
+        mLabel.getPaint().getTextBounds(
+                mLabel.getText().toString(), 0, mLabel.getText().length(), textBounds);
+
+        Rect rect = new Rect(
+                coords[0],
+                coords[1],
+                coords[0] + mSelector.getWidth() + textBounds.width(),
+                coords[1] + Math.max(mSelector.getHeight(), textBounds.height()));
+
+        // If the tap occurred inside icon or the text, these are interactive spots.
+        return rect.contains((int) event.getRawX(), (int) event.getRawY());
+    }
+
+    boolean inSelectRegion(MotionEvent e) {
+        Rect iconRect = new Rect();
+        mSelector.getGlobalVisibleRect(iconRect);
+        return iconRect.contains((int) e.getRawX(), (int) e.getRawY());
+    }
+
+    ItemDetails<Uri> getItemDetails() {
+        return mDetails;
+    }
+
+    @Override
+    public String toString() {
+        return "Item{name:" + mLabel.getText() + ", url:" + mKey + "}";
+    }
+}
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoActivity.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoActivity.java
index c54368b..38e0046 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoActivity.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoActivity.java
@@ -58,6 +58,7 @@
 
     private GridLayoutManager mLayout;
     private int mColumnCount = 1;  // This will get updated when layout changes.
+    private boolean mIterceptListenerEnabled = false;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -66,6 +67,32 @@
         setContentView(R.layout.selection_demo_layout);
         mRecView = (RecyclerView) findViewById(R.id.list);
 
+        // If you want to provided special handling of clicks on items
+        // in RecyclerView (respond to a play button, or show a menu
+        // when a three-dot menu is clicked) you can't just add an OnClickListener
+        // to the View.  This is because Selection lib installs an
+        // OnItemTouchListener w/ RecyclerView, and that listener eats
+        // up many of the touch/mouse events RecyclerView sends its way.
+        // To work around this install your own OnItemTouchListener *before*
+        // you build your SelectionTracker instance. That'll give your listener
+        // a chance to intercept events before Selection lib gobbles them up.
+        mRecView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
+            @Override
+            public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
+                return mIterceptListenerEnabled
+                        && FancyHeaderHolder.isHeader(rv.findChildViewUnder(e.getX(), e.getY()));
+            }
+
+            @Override
+            public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
+                toast(FancySelectionDemoActivity.this, "Clicked on a header!");
+            }
+
+            @Override
+            public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+            }
+        });
+
         mLayout = new GridLayoutManager(this, mColumnCount);
         mRecView.setLayoutManager(mLayout);
         mAdapter = new FancySelectionDemoAdapter(this);
@@ -137,27 +164,19 @@
     @CallSuper
     public boolean onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
-        menu.findItem(R.id.option_menu_add_column).setEnabled(mColumnCount <= 3);
-        menu.findItem(R.id.option_menu_remove_column).setEnabled(mColumnCount > 1);
+        menu.findItem(R.id.option_menu_enable_listener)
+                .setEnabled(!mIterceptListenerEnabled);
+        menu.findItem(R.id.option_menu_disable_listener)
+                .setEnabled(mIterceptListenerEnabled);
         return true;
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.option_menu_add_column:
-                // TODO: Add columns
-                mLayout.setSpanCount(++mColumnCount);
-                return true;
-            case R.id.option_menu_remove_column:
-                mLayout.setSpanCount(--mColumnCount);
-                return true;
-            default:
-                return super.onOptionsItemSelected(item);
-        }
+        mIterceptListenerEnabled = !mIterceptListenerEnabled;
+        return true;
     }
 
-
     @Override
     public void onCreateContextMenu(ContextMenu menu, View v,
             ContextMenu.ContextMenuInfo menuInfo) {
@@ -302,4 +321,4 @@
             return true;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoAdapter.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoAdapter.java
index 5d2f5cb..804e7ec 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoAdapter.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/fancy/FancySelectionDemoAdapter.java
@@ -23,8 +23,9 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.recyclerview.selection.ItemKeyProvider;
 import androidx.recyclerview.selection.SelectionTracker;
 import androidx.recyclerview.widget.RecyclerView;
@@ -32,29 +33,34 @@
 import com.example.android.supportv7.Cheeses;
 import com.example.android.supportv7.R;
 
+import java.util.HashMap;
+import java.util.Map;
+
 final class FancySelectionDemoAdapter extends RecyclerView.Adapter<FancyHolder> {
 
-    private final ContentUriKeyProvider mKeyProvider;
+    public static final int TYPE_HEADER = 1;
+    public static final int TYPE_ITEM = 2;
+
+    private final KeyProvider mKeyProvider;
     private final Context mContext;
 
-    // This should be replaced at "bind" time with a real test that
-    // asks SelectionTracker.
-    private SelectionTest mSelTest;
+    // This default implementation must be replaced
+    // with a real implementation in #bindSelectionHelper.
+    private SelectionTest mSelTest = new SelectionTest() {
+        @Override
+        public boolean isSelected(Uri id) {
+            throw new IllegalStateException(
+                    "Adapter must be initialized with SelectionTracker");
+        }
+    };
 
     FancySelectionDemoAdapter(Context context) {
         mContext = context;
-        mKeyProvider = new ContentUriKeyProvider("cheeses", Cheeses.sCheeseStrings);
-        mSelTest = new SelectionTest() {
-            @Override
-            public boolean isSelected(Uri id) {
-                throw new IllegalStateException(
-                        "Adapter must be initialized with SelectionTracker");
-            }
-        };
+        mKeyProvider = new KeyProvider("cheeses", Cheeses.sCheeseStrings);
 
         // In the fancy edition of selection support we supply access to stable
         // ids using content URI. Since we can map between position and selection key
-        // at will we get fancy dependent functionality like band selection and range support.
+        // at-will we get band selection and range support.
         setHasStableIds(false);
     }
 
@@ -63,12 +69,12 @@
     }
 
     // Glue together SelectionTracker and the adapter.
-    public void bindSelectionHelper(final SelectionTracker<Uri> selectionTracker) {
-        checkArgument(selectionTracker != null);
+    public void bindSelectionHelper(final SelectionTracker<Uri> tracker) {
+        checkArgument(tracker != null);
         mSelTest = new SelectionTest() {
             @Override
             public boolean isSelected(Uri id) {
-                return selectionTracker.isSelected(id);
+                return tracker.isSelected(id);
             }
         };
     }
@@ -83,7 +89,7 @@
 
     @Override
     public int getItemCount() {
-        return Cheeses.sCheeseStrings.length;
+        return mKeyProvider.getCount();
     }
 
     @Override
@@ -92,15 +98,40 @@
     }
 
     @Override
-    public void onBindViewHolder(FancyHolder holder, int position) {
-        Uri uri = mKeyProvider.getKey(position);
-        holder.update(uri, uri.getLastPathSegment(), mSelTest.isSelected(uri));
+    public void onBindViewHolder(@NonNull FancyHolder holder, int position) {
+        if (holder instanceof FancyHeaderHolder) {
+            Uri uri = mKeyProvider.getKey(position);
+            ((FancyHeaderHolder) holder).update(uri.getPathSegments().get(0));
+        } else if (holder instanceof FancyItemHolder) {
+            Uri uri = mKeyProvider.getKey(position);
+            ((FancyItemHolder) holder).update(uri, uri.getPathSegments().get(1),
+                    mSelTest.isSelected(uri));
+        }
     }
 
     @Override
-    public FancyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        LinearLayout layout = inflateLayout(mContext, parent, R.layout.selection_demo_list_item);
-        return new FancyHolder(layout);
+    public int getItemViewType(int position) {
+        Uri key = mKeyProvider.getKey(position);
+        if (key.getPathSegments().size() == 1) {
+            return TYPE_HEADER;
+        } else if (key.getPathSegments().size() == 2) {
+            return TYPE_ITEM;
+        }
+
+        throw new RuntimeException("Unknown view type a position " + position);
+    }
+
+    @Override
+    public FancyHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        switch (viewType) {
+            case TYPE_HEADER:
+                return new FancyHeaderHolder(
+                        inflateLayout(mContext, parent, R.layout.selection_demo_list_header));
+            case TYPE_ITEM:
+                return new FancyItemHolder(
+                        inflateLayout(mContext, parent, R.layout.selection_demo_list_item));
+        }
+        throw new RuntimeException("Unsupported view type" + viewType);
     }
 
     @SuppressWarnings("TypeParameterUnusedInFormals")  // Convenience to avoid clumsy cast.
@@ -113,4 +144,62 @@
     private interface SelectionTest {
         boolean isSelected(Uri id);
     }
+
+    private static final class KeyProvider extends ItemKeyProvider<Uri> {
+
+        private final Uri[] mUris;
+        private final Map<Uri, Integer> mPositions;
+
+        KeyProvider(String authority, String[] values) {
+            // Advise the world we can supply ids/position for entire copus
+            // at any time.
+            super(SCOPE_MAPPED);
+
+            // For the convenience of this demo, we simply trust, based on
+            // past understanding that Cheeses has at least one element
+            // starting with each letter of the English alphabet :)
+            mUris = new Uri[Cheeses.sCheeseStrings.length + 26];
+            mPositions = new HashMap<>();
+
+            char section = '-';  // anything value other than 'a' will do the trick here.
+            int headerOffset = 0;
+
+            for (int i = 0; i < Cheeses.sCheeseStrings.length; i++) {
+                char leadingChar = Cheeses.sCheeseStrings[i].toLowerCase().charAt(0);
+                // When we find a new leading character insert an artificial
+                // cheese header
+                if (leadingChar != section) {
+                    section = leadingChar;
+                    mUris[i + headerOffset] = new Uri.Builder()
+                            .scheme("content")
+                            .encodedAuthority(authority)
+                            .appendPath(Character.toString(section))
+                            .build();
+                    mPositions.put(mUris[i + headerOffset], i + headerOffset);
+                    headerOffset++;
+                }
+                mUris[i + headerOffset] = new Uri.Builder()
+                        .scheme("content")
+                        .encodedAuthority(authority)
+                        .appendPath(Character.toString(section))
+                        .appendPath(Cheeses.sCheeseStrings[i])
+                        .build();
+                mPositions.put(mUris[i + headerOffset], i + headerOffset);
+            }
+        }
+
+        @Override
+        public @Nullable Uri getKey(int position) {
+            return mUris[position];
+        }
+
+        @Override
+        public int getPosition(@NonNull Uri key) {
+            return mPositions.get(key);
+        }
+
+        int getCount() {
+            return mUris.length;
+        }
+    }
 }
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/simple/SimpleSelectionDemoActivity.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/simple/SimpleSelectionDemoActivity.java
index fafddc4..63fda61 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/simple/SimpleSelectionDemoActivity.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/widget/selection/simple/SimpleSelectionDemoActivity.java
@@ -19,12 +19,9 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.widget.Toast;
 
-import androidx.annotation.CallSuper;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.recyclerview.selection.ItemDetailsLookup.ItemDetails;
 import androidx.recyclerview.selection.ItemKeyProvider;
@@ -128,39 +125,6 @@
     }
 
     @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        boolean showMenu = super.onCreateOptionsMenu(menu);
-        getMenuInflater().inflate(R.menu.selection_demo_actions, menu);
-        return showMenu;
-    }
-
-    @Override
-    @CallSuper
-    public boolean onPrepareOptionsMenu(Menu menu) {
-        super.onPrepareOptionsMenu(menu);
-        menu.findItem(R.id.option_menu_add_column).setEnabled(mColumnCount <= 3);
-        menu.findItem(R.id.option_menu_remove_column).setEnabled(mColumnCount > 1);
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.option_menu_add_column:
-                // TODO: Add columns
-                mLayout.setSpanCount(++mColumnCount);
-                return true;
-
-            case R.id.option_menu_remove_column:
-                mLayout.setSpanCount(--mColumnCount);
-                return true;
-
-            default:
-                return super.onOptionsItemSelected(item);
-        }
-    }
-
-    @Override
     public void onBackPressed() {
         if (mSelectionTracker.clearSelection()) {
             return;
diff --git a/samples/Support7Demos/src/main/res/color/selection_demo_item.xml b/samples/Support7Demos/src/main/res/color/selection_demo_item.xml
new file mode 100644
index 0000000..ea11b42
--- /dev/null
+++ b/samples/Support7Demos/src/main/res/color/selection_demo_item.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_activated="true"
+        android:color="?android:attr/colorForegroundInverse"
+        />
+    <item
+        android:state_activated="false"
+        android:color="?android:attr/colorForegroundInverse"
+        android:alpha=".3"
+        />
+</selector>
diff --git a/samples/Support7Demos/src/main/res/color/selection_demo_item_selector.xml b/samples/Support7Demos/src/main/res/color/selection_demo_item_selector.xml
index bd87b4c..c800127 100644
--- a/samples/Support7Demos/src/main/res/color/selection_demo_item_selector.xml
+++ b/samples/Support7Demos/src/main/res/color/selection_demo_item_selector.xml
@@ -16,12 +16,13 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item
-        android:state_activated="true"
-        android:color="?android:attr/colorForeground"
-        />
-    <item
         android:state_activated="false"
         android:color="?android:attr/colorForeground"
         android:alpha=".3"
         />
+    <item
+        android:state_activated="true"
+        android:color="#FFFF0000"
+        android:alpha=".4"
+        />
 </selector>
diff --git a/car-app/app/src/main/AndroidManifest.xml b/samples/Support7Demos/src/main/res/drawable/selection_demo_item.xml
similarity index 77%
rename from car-app/app/src/main/AndroidManifest.xml
rename to samples/Support7Demos/src/main/res/drawable/selection_demo_item.xml
index e1b4910..52d11c5 100644
--- a/car-app/app/src/main/AndroidManifest.xml
+++ b/samples/Support7Demos/src/main/res/drawable/selection_demo_item.xml
@@ -13,4 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest package="androidx.car.app"/>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_activated="true">
+        <color android:color="#22FF0000"></color>
+    </item>
+</selector>
diff --git a/samples/Support7Demos/src/main/res/drawable/selection_demo_item_background.xml b/samples/Support7Demos/src/main/res/drawable/selection_demo_item_background.xml
index e4dbd5f..e15b281 100644
--- a/samples/Support7Demos/src/main/res/drawable/selection_demo_item_background.xml
+++ b/samples/Support7Demos/src/main/res/drawable/selection_demo_item_background.xml
@@ -16,6 +16,6 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_activated="true">
-        <color android:color="#220000FF"></color>
+        <color android:color="#2200FF00"></color>
     </item>
 </selector>
diff --git a/samples/Support7Demos/src/main/res/layout/selection_demo_list_header.xml b/samples/Support7Demos/src/main/res/layout/selection_demo_list_header.xml
new file mode 100644
index 0000000..13e127a
--- /dev/null
+++ b/samples/Support7Demos/src/main/res/layout/selection_demo_list_header.xml
@@ -0,0 +1,42 @@
+<?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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:paddingStart="10dp"
+    android:paddingEnd="10dp"
+    android:paddingTop="5dp"
+    android:paddingBottom="5dp"
+    android:layout_height="50dp">
+  <LinearLayout
+      android:id="@+id/container"
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_height="match_parent"
+      android:layout_width="match_parent"
+      android:background="@drawable/selection_demo_item_background">
+      <TextView
+          android:id="@+id/label"
+          android:textSize="30sp"
+          android:textStyle="bold"
+          android:gravity="center_vertical"
+          android:paddingStart="10dp"
+          android:paddingEnd="10dp"
+          android:layout_height="match_parent"
+          android:layout_width="match_parent">
+      </TextView>
+  </LinearLayout>
+</LinearLayout>
diff --git a/samples/Support7Demos/src/main/res/layout/selection_demo_list_item.xml b/samples/Support7Demos/src/main/res/layout/selection_demo_list_item.xml
index 0d4b718..fb5e8e9 100644
--- a/samples/Support7Demos/src/main/res/layout/selection_demo_list_item.xml
+++ b/samples/Support7Demos/src/main/res/layout/selection_demo_list_item.xml
@@ -27,11 +27,10 @@
       xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_height="match_parent"
       android:layout_width="match_parent"
-      android:background="@drawable/selection_demo_item_background">
+      android:background="#FFFFFFFF">
       <TextView
           android:id="@+id/selector"
           android:textSize="20sp"
-          android:textStyle="bold"
           android:gravity="center"
           android:layout_height="match_parent"
           android:layout_width="40dp"
@@ -47,7 +46,8 @@
           android:paddingStart="10dp"
           android:paddingEnd="10dp"
           android:layout_height="match_parent"
-          android:layout_width="match_parent">
+          android:layout_width="wrap_content"
+          android:background="@drawable/selection_demo_item">
       </TextView>
   </LinearLayout>
 </LinearLayout>
diff --git a/samples/Support7Demos/src/main/res/menu/selection_demo_actions.xml b/samples/Support7Demos/src/main/res/menu/selection_demo_actions.xml
index 484d8b6..ce76ddc3 100644
--- a/samples/Support7Demos/src/main/res/menu/selection_demo_actions.xml
+++ b/samples/Support7Demos/src/main/res/menu/selection_demo_actions.xml
@@ -16,9 +16,9 @@
 
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
-       android:id="@+id/option_menu_add_column"
-       android:title="Add column" />
+       android:id="@+id/option_menu_enable_listener"
+       android:title="Enable OnItemTouchListener" />
    <item
-       android:id="@+id/option_menu_remove_column"
-       android:title="Remove column" />
+       android:id="@+id/option_menu_disable_listener"
+       android:title="Disable OnItemTouchListener" />
 </menu>
diff --git a/samples/SupportTransitionDemos/build.gradle b/samples/SupportTransitionDemos/build.gradle
index d8ba71c..9838c8c 100644
--- a/samples/SupportTransitionDemos/build.gradle
+++ b/samples/SupportTransitionDemos/build.gradle
@@ -7,7 +7,7 @@
     implementation(project(":transition:transition"))
     implementation(project(":appcompat:appcompat"))
     implementation(project(":recyclerview:recyclerview"))
-    implementation(project(":interpolator"))
+    implementation(project(":interpolator:interpolator"))
 }
 
 android {
diff --git a/savedstate/api/1.0.0-alpha01.txt b/savedstate/savedstate/api/1.0.0-alpha01.txt
similarity index 100%
rename from savedstate/api/1.0.0-alpha01.txt
rename to savedstate/savedstate/api/1.0.0-alpha01.txt
diff --git a/savedstate/api/1.0.0-alpha02.txt b/savedstate/savedstate/api/1.0.0-alpha02.txt
similarity index 100%
rename from savedstate/api/1.0.0-alpha02.txt
rename to savedstate/savedstate/api/1.0.0-alpha02.txt
diff --git a/savedstate/api/1.0.0-alpha03.txt b/savedstate/savedstate/api/1.0.0-alpha03.txt
similarity index 100%
rename from savedstate/api/1.0.0-alpha03.txt
rename to savedstate/savedstate/api/1.0.0-alpha03.txt
diff --git a/savedstate/api/1.0.0-beta00.txt b/savedstate/savedstate/api/1.0.0-beta00.txt
similarity index 100%
rename from savedstate/api/1.0.0-beta00.txt
rename to savedstate/savedstate/api/1.0.0-beta00.txt
diff --git a/savedstate/api/1.0.0-beta01.txt b/savedstate/savedstate/api/1.0.0-beta01.txt
similarity index 100%
rename from savedstate/api/1.0.0-beta01.txt
rename to savedstate/savedstate/api/1.0.0-beta01.txt
diff --git a/savedstate/api/1.0.0-rc01.txt b/savedstate/savedstate/api/1.0.0-rc01.txt
similarity index 100%
rename from savedstate/api/1.0.0-rc01.txt
rename to savedstate/savedstate/api/1.0.0-rc01.txt
diff --git a/savedstate/api/1.1.0-alpha01.txt b/savedstate/savedstate/api/1.1.0-alpha01.txt
similarity index 100%
rename from savedstate/api/1.1.0-alpha01.txt
rename to savedstate/savedstate/api/1.1.0-alpha01.txt
diff --git a/savedstate/api/current.txt b/savedstate/savedstate/api/current.txt
similarity index 100%
rename from savedstate/api/current.txt
rename to savedstate/savedstate/api/current.txt
diff --git a/savedstate/api/public_plus_experimental_1.0.0-alpha02.txt b/savedstate/savedstate/api/public_plus_experimental_1.0.0-alpha02.txt
similarity index 100%
rename from savedstate/api/public_plus_experimental_1.0.0-alpha02.txt
rename to savedstate/savedstate/api/public_plus_experimental_1.0.0-alpha02.txt
diff --git a/savedstate/api/public_plus_experimental_1.0.0-beta01.txt b/savedstate/savedstate/api/public_plus_experimental_1.0.0-beta01.txt
similarity index 100%
rename from savedstate/api/public_plus_experimental_1.0.0-beta01.txt
rename to savedstate/savedstate/api/public_plus_experimental_1.0.0-beta01.txt
diff --git a/savedstate/api/public_plus_experimental_1.0.0-rc01.txt b/savedstate/savedstate/api/public_plus_experimental_1.0.0-rc01.txt
similarity index 100%
rename from savedstate/api/public_plus_experimental_1.0.0-rc01.txt
rename to savedstate/savedstate/api/public_plus_experimental_1.0.0-rc01.txt
diff --git a/savedstate/api/public_plus_experimental_1.1.0-alpha01.txt b/savedstate/savedstate/api/public_plus_experimental_1.1.0-alpha01.txt
similarity index 100%
rename from savedstate/api/public_plus_experimental_1.1.0-alpha01.txt
rename to savedstate/savedstate/api/public_plus_experimental_1.1.0-alpha01.txt
diff --git a/savedstate/api/public_plus_experimental_current.txt b/savedstate/savedstate/api/public_plus_experimental_current.txt
similarity index 100%
rename from savedstate/api/public_plus_experimental_current.txt
rename to savedstate/savedstate/api/public_plus_experimental_current.txt
diff --git a/savedstate/api/res-1.0.0-alpha01.txt b/savedstate/savedstate/api/res-1.0.0-alpha01.txt
similarity index 100%
rename from savedstate/api/res-1.0.0-alpha01.txt
rename to savedstate/savedstate/api/res-1.0.0-alpha01.txt
diff --git a/savedstate/api/res-1.0.0-alpha02.txt b/savedstate/savedstate/api/res-1.0.0-alpha02.txt
similarity index 100%
rename from savedstate/api/res-1.0.0-alpha02.txt
rename to savedstate/savedstate/api/res-1.0.0-alpha02.txt
diff --git a/savedstate/api/res-1.0.0-alpha03.txt b/savedstate/savedstate/api/res-1.0.0-alpha03.txt
similarity index 100%
rename from savedstate/api/res-1.0.0-alpha03.txt
rename to savedstate/savedstate/api/res-1.0.0-alpha03.txt
diff --git a/savedstate/api/res-1.0.0-beta00.txt b/savedstate/savedstate/api/res-1.0.0-beta00.txt
similarity index 100%
rename from savedstate/api/res-1.0.0-beta00.txt
rename to savedstate/savedstate/api/res-1.0.0-beta00.txt
diff --git a/savedstate/api/res-1.0.0-beta01.txt b/savedstate/savedstate/api/res-1.0.0-beta01.txt
similarity index 100%
rename from savedstate/api/res-1.0.0-beta01.txt
rename to savedstate/savedstate/api/res-1.0.0-beta01.txt
diff --git a/savedstate/api/res-1.0.0-rc01.txt b/savedstate/savedstate/api/res-1.0.0-rc01.txt
similarity index 100%
rename from savedstate/api/res-1.0.0-rc01.txt
rename to savedstate/savedstate/api/res-1.0.0-rc01.txt
diff --git a/savedstate/api/res-1.1.0-alpha01.txt b/savedstate/savedstate/api/res-1.1.0-alpha01.txt
similarity index 100%
rename from savedstate/api/res-1.1.0-alpha01.txt
rename to savedstate/savedstate/api/res-1.1.0-alpha01.txt
diff --git a/savedstate/api/restricted_1.0.0-alpha01.txt b/savedstate/savedstate/api/restricted_1.0.0-alpha01.txt
similarity index 100%
rename from savedstate/api/restricted_1.0.0-alpha01.txt
rename to savedstate/savedstate/api/restricted_1.0.0-alpha01.txt
diff --git a/savedstate/api/restricted_1.0.0-alpha02.txt b/savedstate/savedstate/api/restricted_1.0.0-alpha02.txt
similarity index 100%
rename from savedstate/api/restricted_1.0.0-alpha02.txt
rename to savedstate/savedstate/api/restricted_1.0.0-alpha02.txt
diff --git a/savedstate/api/restricted_1.0.0-alpha03.txt b/savedstate/savedstate/api/restricted_1.0.0-alpha03.txt
similarity index 100%
rename from savedstate/api/restricted_1.0.0-alpha03.txt
rename to savedstate/savedstate/api/restricted_1.0.0-alpha03.txt
diff --git a/savedstate/api/restricted_1.0.0-beta00.txt b/savedstate/savedstate/api/restricted_1.0.0-beta00.txt
similarity index 100%
rename from savedstate/api/restricted_1.0.0-beta00.txt
rename to savedstate/savedstate/api/restricted_1.0.0-beta00.txt
diff --git a/savedstate/api/restricted_1.0.0-beta01.txt b/savedstate/savedstate/api/restricted_1.0.0-beta01.txt
similarity index 100%
rename from savedstate/api/restricted_1.0.0-beta01.txt
rename to savedstate/savedstate/api/restricted_1.0.0-beta01.txt
diff --git a/savedstate/api/restricted_1.0.0-rc01.txt b/savedstate/savedstate/api/restricted_1.0.0-rc01.txt
similarity index 100%
rename from savedstate/api/restricted_1.0.0-rc01.txt
rename to savedstate/savedstate/api/restricted_1.0.0-rc01.txt
diff --git a/savedstate/api/restricted_1.1.0-alpha01.txt b/savedstate/savedstate/api/restricted_1.1.0-alpha01.txt
similarity index 100%
rename from savedstate/api/restricted_1.1.0-alpha01.txt
rename to savedstate/savedstate/api/restricted_1.1.0-alpha01.txt
diff --git a/savedstate/api/restricted_current.txt b/savedstate/savedstate/api/restricted_current.txt
similarity index 100%
rename from savedstate/api/restricted_current.txt
rename to savedstate/savedstate/api/restricted_current.txt
diff --git a/savedstate/build.gradle b/savedstate/savedstate/build.gradle
similarity index 100%
rename from savedstate/build.gradle
rename to savedstate/savedstate/build.gradle
diff --git a/savedstate/proguard-rules.pro b/savedstate/savedstate/proguard-rules.pro
similarity index 100%
rename from savedstate/proguard-rules.pro
rename to savedstate/savedstate/proguard-rules.pro
diff --git a/savedstate/src/androidTest/AndroidManifest.xml b/savedstate/savedstate/src/androidTest/AndroidManifest.xml
similarity index 100%
rename from savedstate/src/androidTest/AndroidManifest.xml
rename to savedstate/savedstate/src/androidTest/AndroidManifest.xml
diff --git a/savedstate/src/androidTest/java/androidx/savedstate/ErrorInStaticBlock.java b/savedstate/savedstate/src/androidTest/java/androidx/savedstate/ErrorInStaticBlock.java
similarity index 100%
rename from savedstate/src/androidTest/java/androidx/savedstate/ErrorInStaticBlock.java
rename to savedstate/savedstate/src/androidTest/java/androidx/savedstate/ErrorInStaticBlock.java
diff --git a/savedstate/src/androidTest/java/androidx/savedstate/SavedStateRegistryTest.kt b/savedstate/savedstate/src/androidTest/java/androidx/savedstate/SavedStateRegistryTest.kt
similarity index 100%
rename from savedstate/src/androidTest/java/androidx/savedstate/SavedStateRegistryTest.kt
rename to savedstate/savedstate/src/androidTest/java/androidx/savedstate/SavedStateRegistryTest.kt
diff --git a/savedstate/src/main/AndroidManifest.xml b/savedstate/savedstate/src/main/AndroidManifest.xml
similarity index 100%
rename from savedstate/src/main/AndroidManifest.xml
rename to savedstate/savedstate/src/main/AndroidManifest.xml
diff --git a/savedstate/src/main/java/androidx/savedstate/Recreator.java b/savedstate/savedstate/src/main/java/androidx/savedstate/Recreator.java
similarity index 100%
rename from savedstate/src/main/java/androidx/savedstate/Recreator.java
rename to savedstate/savedstate/src/main/java/androidx/savedstate/Recreator.java
diff --git a/savedstate/src/main/java/androidx/savedstate/SavedStateRegistry.java b/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistry.java
similarity index 100%
rename from savedstate/src/main/java/androidx/savedstate/SavedStateRegistry.java
rename to savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistry.java
diff --git a/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryController.java b/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryController.java
similarity index 100%
rename from savedstate/src/main/java/androidx/savedstate/SavedStateRegistryController.java
rename to savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryController.java
diff --git a/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryOwner.java b/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryOwner.java
similarity index 100%
rename from savedstate/src/main/java/androidx/savedstate/SavedStateRegistryOwner.java
rename to savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistryOwner.java
diff --git a/settings.gradle b/settings.gradle
index 5072ddc..686c4f6 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -94,7 +94,6 @@
 includeProject(":camera:integration-tests:camera-testapp-view", "camera/integration-tests/viewtestapp")
 includeProject(":camera:integration-tests:camera-testlib-extensions", "camera/integration-tests/extensionstestlib")
 includeProject(":car", "car/core")
-includeProject(":car-app:app", "car-app/app")
 includeProject(":car-moderator", "car/moderator")
 includeProject(":cardview:cardview", "cardview/cardview")
 includeProject(":collection:collection", "collection/collection")
@@ -131,7 +130,7 @@
 includeProject(":inspection:inspection", "inspection/inspection")
 includeProject(":inspection:inspection-gradle-plugin", "inspection/inspection-gradle-plugin")
 includeProject(":inspection:inspection-testing", "inspection/inspection-testing")
-includeProject(":interpolator", "interpolator")
+includeProject(":interpolator:interpolator", "interpolator/interpolator")
 includeProject(":jetifier-core", "jetifier/jetifier/core")
 includeProject(":jetifier-processor", "jetifier/jetifier/processor")
 includeProject(":jetifier-standalone", "jetifier/jetifier/standalone")
@@ -201,6 +200,7 @@
 includeProject(":paging:paging-rxjava2", "paging/rxjava2")
 includeProject(":paging:paging-rxjava2-ktx", "paging/rxjava2/ktx")
 includeProject(":paging:paging-guava", "paging/guava")
+includeProject(":paging:paging-testutils", "paging/testutils")
 includeProject(":palette:palette", "palette/palette")
 includeProject(":palette:palette-ktx", "palette/palette-ktx")
 includeProject(":percentlayout:percentlayout", "percentlayout/percentlayout")
@@ -229,7 +229,7 @@
 includeProject(":remotecallback", "remotecallback")
 includeProject(":versionedparcelable-annotation", "versionedparcelable/annotation")
 includeProject(":versionedparcelable", "versionedparcelable")
-includeProject(":savedstate", "savedstate")
+includeProject(":savedstate:savedstate", "savedstate/savedstate")
 includeProject(":security:security-crypto", "security/crypto")
 includeProject(":security:security-identity-credential", "security/identity-credential")
 includeProject(":serialization:serialization", "serialization/serialization")
@@ -262,7 +262,7 @@
 includeProject(":vectordrawable:vectordrawable", "vectordrawable/vectordrawable")
 includeProject(":vectordrawable:vectordrawable-animated", "vectordrawable/vectordrawable-animated")
 includeProject(":viewpager", "viewpager")
-includeProject(":viewpager2", "viewpager2")
+includeProject(":viewpager2:viewpager2", "viewpager2/viewpager2")
 includeProject(":viewpager2:integration-tests:testapp", "viewpager2/integration-tests/testapp")
 includeProject(":wear:wear", "wear/wear")
 includeProject(":webkit:webkit", "webkit/webkit")
@@ -322,6 +322,7 @@
 includeProject(":internal-testutils-truth", "testutils/testutils-truth")
 includeProject(":internal-testutils-ktx", "testutils/testutils-ktx")
 includeProject(":internal-testutils-navigation", "testutils/testutils-navigation")
+includeProject(":internal-testutils-gradle-plugin", "testutils/testutils-gradle-plugin")
 
 /////////////////////////////
 //
diff --git a/sqlite/sqlite-inspection/src/androidTest/java/SqliteInspectorTest.kt b/sqlite/sqlite-inspection/src/androidTest/java/SqliteInspectorTest.kt
deleted file mode 100644
index cbf6ff5..0000000
--- a/sqlite/sqlite-inspection/src/androidTest/java/SqliteInspectorTest.kt
+++ /dev/null
@@ -1,492 +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.sqlite.inspection
-
-import android.database.sqlite.SQLiteDatabase
-import android.database.sqlite.SQLiteOpenHelper
-import androidx.inspection.InspectorEnvironment
-import androidx.inspection.InspectorEnvironment.EntryHook
-import androidx.inspection.InspectorEnvironment.ExitHook
-import androidx.inspection.testing.InspectorTester
-import androidx.sqlite.inspection.SqliteInspectorProtocol.CellValue
-import androidx.sqlite.inspection.SqliteInspectorProtocol.CellValue.ValueCase
-import androidx.sqlite.inspection.SqliteInspectorProtocol.Command
-import androidx.sqlite.inspection.SqliteInspectorProtocol.Event
-import androidx.sqlite.inspection.SqliteInspectorProtocol.GetSchemaCommand
-import androidx.sqlite.inspection.SqliteInspectorProtocol.Response
-import androidx.sqlite.inspection.SqliteInspectorProtocol.Row
-import androidx.sqlite.inspection.SqliteInspectorProtocol.TrackDatabasesCommand
-import androidx.sqlite.inspection.SqliteInspectorProtocol.TrackDatabasesResponse
-import androidx.sqlite.inspection.SqliteInspectorTest.FakeInspectorEnvironment.RegisterHookEntry.EntryHookEntry
-import androidx.sqlite.inspection.SqliteInspectorTest.FakeInspectorEnvironment.RegisterHookEntry.ExitHookEntry
-import androidx.sqlite.inspection.SqliteInspectorTest.MessageFactory.createTrackDatabasesCommand
-import androidx.sqlite.inspection.SqliteInspectorTest.MessageFactory.createTrackDatabasesResponse
-import androidx.sqlite.inspection.SqliteInspectorTest.MessageFactory.createGetSchemaCommand
-import androidx.sqlite.inspection.SqliteInspectorTest.MessageFactory.createQueryTableCommand
-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 kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.runBlocking
-import org.junit.Rule
-import org.junit.Test
-import org.junit.rules.TemporaryFolder
-import org.junit.runner.RunWith
-
-@MediumTest
-@RunWith(AndroidJUnit4::class)
-@ExperimentalCoroutinesApi
-class SqliteInspectorTest {
-    @get:Rule
-    val tempDirectory = TemporaryFolder()
-
-    @Test
-    fun test_basic_proto() {
-        val command = createTrackDatabasesCommand()
-
-        val commandBytes = command.toByteArray()
-        assertThat(commandBytes).isNotEmpty()
-
-        val commandBack = Command.parseFrom(commandBytes)
-        assertThat(commandBack).isEqualTo(command)
-    }
-
-    @Test
-    fun test_basic_inject() = runBlocking {
-        TestEnvironment.setUp().run {
-            // no crash means the inspector was successfully injected
-            assertNoQueuedEvents()
-            dispose()
-        }
-    }
-
-    @Test
-    fun test_track_databases() = runBlocking {
-        val alreadyOpenDatabases = listOf(Database("db1"), Database("db2"))
-
-        TestEnvironment.setUp(alreadyOpenDatabases).run {
-            sendCommand(createTrackDatabasesCommand()).let { response ->
-                assertThat(response).isEqualTo(createTrackDatabasesResponse())
-            }
-
-            // evaluate 'already-open' instances are found
-            alreadyOpenDatabases.let { expected ->
-                val actual = expected.indices.map { receiveEvent().databaseOpened }
-                assertNoQueuedEvents()
-                assertThat(actual.map { it.databaseId }.distinct()).hasSize(expected.size)
-                expected.forEachIndexed { ix, _ ->
-                    assertThat(actual[ix].name).isEqualTo(expected[ix].path)
-                }
-            }
-
-            // evaluate registered hooks
-            val hookEntries = consumeRegisteredHooks()
-            assertThat(hookEntries).hasSize(1)
-            hookEntries.first().let { entry ->
-                // expect one exit hook tracking database open events
-                assertThat(entry).isInstanceOf(ExitHookEntry::class.java)
-                assertThat(entry.originClass.name).isEqualTo(SQLiteDatabase::class.java.name)
-                assertThat(entry.originMethod)
-                    .isEqualTo(SqliteInspector.sOpenDatabaseCommandSignature)
-
-                // verify that executing the registered hook will result in tracking events
-                assertNoQueuedEvents()
-                @Suppress("UNCHECKED_CAST")
-                val exitHook = (entry as ExitHookEntry).exitHook as ExitHook<SQLiteDatabase>
-                val database = Database("db3").instance
-                assertThat(exitHook.onExit(database)).isSameInstanceAs(database)
-                receiveEvent().let { event ->
-                    assertThat(event.databaseOpened.name).isEqualTo(database.path)
-                }
-            }
-
-            assertThat(consumeRegisteredHooks()).isEmpty()
-            dispose()
-        }
-    }
-
-    @Test
-    fun test_get_schema_complex_tables() {
-        test_get_schema(
-            listOf(
-                Database(
-                    "db1",
-                    Table(
-                        "table1",
-                        Column("t", "TEXT"),
-                        Column("nu", "NUMERIC"),
-                        Column("i", "INTEGER"),
-                        Column("r", "REAL"),
-                        Column("b", "BLOB")
-                    ),
-                    Table(
-                        "table2",
-                        Column("id", "INTEGER"),
-                        Column("name", "TEXT")
-
-                    )
-                )
-            )
-        )
-    }
-
-    @Test
-    fun test_get_schema_multiple_databases() {
-        test_get_schema(
-            listOf(
-                Database("db3", Table("t3", Column("c3", "BLOB"))),
-                Database("db2", Table("t2", Column("c2", "TEXT"))),
-                Database("db1", Table("t1", Column("c1", "TEXT")))
-            )
-        )
-    }
-
-    private fun test_get_schema(alreadyOpenDatabases: List<Database>) = runBlocking {
-        assertThat(alreadyOpenDatabases).isNotEmpty()
-
-        // prepare test environment
-        TestEnvironment.setUp(alreadyOpenDatabases).run {
-            sendCommand(createTrackDatabasesCommand())
-            val databaseConnections =
-                alreadyOpenDatabases.indices.map { receiveEvent().databaseOpened }
-
-            val schemas =
-                databaseConnections
-                    .sortedBy { it.name }
-                    .map { sendCommand(createGetSchemaCommand(it.databaseId)).getSchema }
-
-            alreadyOpenDatabases
-                .sortedBy { it.path }
-                .forEach2(schemas) { expectedSchema, actualSchema ->
-                    val expectedTables = expectedSchema.tables.sortedBy { it.name }
-                    val actualTables = actualSchema.tablesList.sortedBy { it.name }
-
-                    expectedTables.forEach2(actualTables) { expectedTable, actualTable ->
-                        assertThat(actualTable.name).isEqualTo(expectedTable.name)
-
-                        val expectedColumns = expectedTable.columns.sortedBy { it.name }
-                        val actualColumns = actualTable.columnsList.sortedBy { it.name }
-
-                        expectedColumns.forEach2(actualColumns) { expectedColumn, actualColumn ->
-                            assertThat(actualColumn.name).isEqualTo(expectedColumn.name)
-                            assertThat(actualColumn.type).isEqualTo(expectedColumn.type)
-                        }
-                }
-            }
-
-            dispose()
-        }
-    }
-
-    @Test
-    fun test_query() = runBlocking {
-        // TODO: add tests for invalid queries
-        // TODO: add tests spanning multiple tables (e.g. union of two queries)
-        // TODO: split into smaller tests / test class
-
-        // given
-        val table1 = Table(
-            "table1",
-            Column("t", "TEXT"),
-            Column("nu", "NUMERIC"),
-            Column("i", "INTEGER"),
-            Column("r", "REAL"),
-            Column("b", "BLOB")
-        )
-
-        val table2 = Table(
-            "table2",
-            Column("id", "INTEGER"),
-            Column("name", "TEXT")
-        )
-
-        val database = Database("db1", table1, table2)
-
-        TestEnvironment.setUp(
-            alreadyOpenDatabases = listOf(
-                Database("ignored_1"),
-                database,
-                Database("ignored_2")
-            )
-        ).run {
-            database.insertValues(table1, *repeat5("'500.0'"))
-            database.insertValues(table1, *repeat5("500.0"))
-            database.insertValues(table1, *repeat5("500"))
-            database.insertValues(table1, *repeat5("x'0500'"))
-            database.insertValues(table1, *repeat5("NULL"))
-
-            database.insertValues(table2, "1", "'A'")
-            database.insertValues(table2, "2", "'B'")
-            database.insertValues(table2, "3", "'C'")
-            database.insertValues(table2, "4", "'D'")
-
-            sendCommand(createTrackDatabasesCommand())
-            val databaseId = findDatabaseId(database.path)
-
-            val query = "select * from ${table1.name}"
-
-            val expectedValues = listOf(
-                listOf("500.0", 500, 500, 500.0f, "500.0"), // text|integer|integer|float|text
-                listOf("500.0", 500, 500, 500.0f, 500.0f), // text|integer|integer|float|float
-                listOf("500", 500, 500, 500.0f, 500), // text|integer|integer|float|integer
-                listOf(*repeat5(arrayOf<Any?>(5.toByte(), 0.toByte()))), // blob|blob|blob|blob|blob
-                listOf(*repeat5<Any?>(null)) // null|null|null|null|null
-            )
-
-            val expectedTypes = listOf(
-                listOf("text", "integer", "integer", "float", "text"),
-                listOf("text", "integer", "integer", "float", "float"),
-                listOf("text", "integer", "integer", "float", "integer"),
-                listOf("blob", "blob", "blob", "blob", "blob"),
-                listOf("null", "null", "null", "null", "null")
-            )
-
-            // when
-            sendCommand(createQueryTableCommand(databaseId, query)).let { response ->
-                // then
-                response.query.rowsList.let { actualRows: List<Row> ->
-                    actualRows.forEachIndexed { rowIx, row ->
-                        row.valuesList.forEachIndexed { colIx, cell ->
-                            assertThat(cell.value).isEqualTo(expectedValues[rowIx][colIx])
-                            assertThat(cell.type).isEqualTo(expectedTypes[rowIx][colIx])
-                            assertThat(cell.columnName).isEqualTo(table1.columns[colIx].name)
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    private class TestEnvironment private constructor(
-        private val inspectorTester: InspectorTester,
-        private val environment: FakeInspectorEnvironment
-    ) {
-        suspend fun sendCommand(command: Command): Response =
-            inspectorTester.sendCommand(command.toByteArray())
-                .let { responseBytes ->
-                    assertThat(responseBytes).isNotEmpty()
-                    Response.parseFrom(responseBytes)
-                }
-
-        suspend fun receiveEvent(): Event =
-            inspectorTester.channel.receive().let { responseBytes ->
-                assertThat(responseBytes).isNotEmpty()
-                Event.parseFrom(responseBytes)
-            }
-
-        /** Assumes an event with the relevant database will be fired. */
-        suspend fun findDatabaseId(databaseName: String): Int {
-            while (true) {
-                val event = receiveEvent().databaseOpened
-                if (event.name == databaseName) {
-                    return event.databaseId
-                }
-            }
-        }
-
-        fun consumeRegisteredHooks(): List<FakeInspectorEnvironment.RegisterHookEntry> =
-            environment.consumeRegisteredHooks()
-
-        fun dispose() {
-            assertNoQueuedEvents() // remove if doesn't match future test design
-            inspectorTester.dispose()
-        }
-
-        fun assertNoQueuedEvents() {
-            assertThat(inspectorTester.channel.isEmpty).isTrue()
-        }
-
-        /** Iterates over two lists of the same size */
-        fun <A, B> List<A>.forEach2(other: List<B>, action: (A, B) -> Unit) {
-            assertThat(this.size).isEqualTo(other.size)
-            zip(other, action)
-        }
-
-        inline fun <reified T> repeatN(value: T, n: Int): Array<T> =
-            (0 until n).map { value }.toTypedArray()
-
-        inline fun <reified T> repeat5(v: T) = repeatN(v, 5)
-
-        val (CellValue).value: Any? get() = valueType.first
-        val (CellValue).type: String get() = valueType.second
-        val (CellValue).valueType: Pair<Any?, String>
-            get() = when (valueCase) {
-                ValueCase.STRING_VALUE -> stringValue to "text"
-                ValueCase.INT_VALUE -> intValue to "integer"
-                ValueCase.FLOAT_VALUE -> floatValue to "float"
-                ValueCase.BLOB_VALUE -> blobValue.toByteArray().toTypedArray() to "blob"
-                ValueCase.VALUE_NOT_SET -> null to "null"
-                else -> throw IllegalArgumentException()
-            }
-
-        companion object {
-            suspend fun setUp(alreadyOpenDatabases: List<Database> = emptyList()): TestEnvironment {
-                // prepare test environment
-                val environment = FakeInspectorEnvironment()
-                val inspectorTester =
-                    InspectorTester(SqliteInspectorFactory.SQLITE_INSPECTOR_ID, environment)
-                // prepare test environment: registered hooks
-                assertThat(environment.consumeRegisteredHooks()).isEmpty()
-                // prepare test environment: register 'already open' instances
-                environment.registerInstancesToFind(alreadyOpenDatabases.map {
-                    it.instance
-                })
-                return TestEnvironment(inspectorTester, environment)
-            }
-        }
-    }
-
-    private inner class Database(name: String, val tables: List<Table>) {
-        constructor(name: String, vararg tables: Table) : this(name, tables.toList())
-
-        val path: String = tempDirectory.newFile(name).absolutePath
-
-        val instance: SQLiteDatabase by lazy {
-            createDatabase().also { db -> tables.forEach { t -> db.addTable(t) } }
-        }
-
-        fun insertValues(table: Table, vararg values: String) {
-            assertThat(values).isNotEmpty()
-            assertThat(values).hasLength(table.columns.size)
-            instance.insertValues(table.name, values.toList())
-        }
-
-        private fun createDatabase(): SQLiteDatabase {
-            val context = ApplicationProvider.getApplicationContext() as android.content.Context
-            val openHelper = object : SQLiteOpenHelper(context, path, null, 1) {
-                override fun onCreate(db: SQLiteDatabase?) = Unit
-                override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) = Unit
-            }
-            return openHelper.readableDatabase
-        }
-
-        private fun SQLiteDatabase.addTable(table: Table) = execSQL(table.toCreateString())
-
-        private fun SQLiteDatabase.insertValues(tableName: String, values: List<String>) {
-            execSQL(values.joinToString(
-                prefix = "INSERT INTO $tableName VALUES(",
-                postfix = ");"
-            ) { it })
-        }
-    }
-
-    private data class Table(val name: String, val columns: List<Column>) {
-        constructor(name: String, vararg columns: Column) : this(name, columns.toList())
-
-        init {
-            assertThat(columns).isNotEmpty()
-        }
-
-        fun toCreateString(): String =
-            columns.joinToString(
-                prefix = "CREATE TABLE $name (",
-                postfix = ");"
-            ) { "${it.name} ${it.type}" }
-    }
-
-    private data class Column(val name: String, val type: String)
-
-    private object MessageFactory {
-        fun createTrackDatabasesCommand(): Command =
-            Command.newBuilder().setTrackDatabases(
-                TrackDatabasesCommand.getDefaultInstance()
-            ).build()
-
-        fun createTrackDatabasesResponse(): Response =
-            Response.newBuilder().setTrackDatabases(
-                TrackDatabasesResponse.getDefaultInstance()
-            ).build()
-
-        fun createGetSchemaCommand(databaseId: Int): Command =
-            Command.newBuilder().setGetSchema(
-                GetSchemaCommand.newBuilder().setDatabaseId(databaseId).build()
-            ).build()
-
-        fun createQueryTableCommand(
-            databaseId: Int,
-            query: String
-        ): Command =
-            Command.newBuilder().setQuery(
-                SqliteInspectorProtocol.QueryCommand.newBuilder()
-                    .setDatabaseId(databaseId)
-                    .setQuery(query)
-                    .build()
-            ).build()
-    }
-
-    /**
-     * Fake inspector environment with the following behaviour:
-     * - [findInstances] returns pre-registered values from [registerInstancesToFind].
-     * - [registerEntryHook] and [registerExitHook] record the calls which can later be
-     * retrieved in [consumeRegisteredHooks].
-     */
-    private class FakeInspectorEnvironment : InspectorEnvironment {
-        private val instancesToFind = mutableListOf<Any>()
-        private val registeredHooks = mutableListOf<RegisterHookEntry>()
-
-        fun registerInstancesToFind(instances: List<Any>) {
-            instancesToFind.addAll(instances)
-        }
-
-        /**
-         *  Returns instances pre-registered in [registerInstancesToFind].
-         *  By design crashes in case of the wrong setup - indicating an issue with test code.
-         */
-        @Suppress("UNCHECKED_CAST")
-        // TODO: implement actual findInstances behaviour
-        override fun <T : Any?> findInstances(clazz: Class<T>): MutableList<T> =
-            instancesToFind.map { it as T }.toMutableList()
-
-        override fun registerEntryHook(
-            originClass: Class<*>,
-            originMethod: String,
-            entryHook: EntryHook
-        ) {
-            // TODO: implement actual registerEntryHook behaviour
-            registeredHooks.add(EntryHookEntry(originClass, originMethod, entryHook))
-        }
-
-        override fun <T : Any?> registerExitHook(
-            originClass: Class<*>,
-            originMethod: String,
-            exitHook: ExitHook<T>
-        ) {
-            // TODO: implement actual registerExitHook behaviour
-            registeredHooks.add(ExitHookEntry(originClass, originMethod, exitHook))
-        }
-
-        fun consumeRegisteredHooks(): List<RegisterHookEntry> =
-            registeredHooks.toList().also {
-                registeredHooks.clear()
-            }
-
-        sealed class RegisterHookEntry(val originClass: Class<*>, val originMethod: String) {
-            class ExitHookEntry(
-                originClass: Class<*>,
-                originMethod: String,
-                val exitHook: ExitHook<*>
-            ) : RegisterHookEntry(originClass, originMethod)
-
-            class EntryHookEntry(
-                originClass: Class<*>,
-                originMethod: String,
-                @Suppress("unused") val entryHook: EntryHook
-            ) : RegisterHookEntry(originClass, originMethod)
-        }
-    }
-}
diff --git a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/BasicTest.kt b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/BasicTest.kt
new file mode 100644
index 0000000..a95b53f
--- /dev/null
+++ b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/BasicTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.sqlite.inspection.test
+
+import androidx.sqlite.inspection.SqliteInspectorProtocol
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@ExperimentalCoroutinesApi
+class BasicTest {
+    @get:Rule
+    val testEnvironment = SqliteInspectorTestEnvironment()
+
+    @Test
+    fun test_basic_proto() {
+        val command = MessageFactory.createTrackDatabasesCommand()
+
+        val commandBytes = command.toByteArray()
+        assertThat(commandBytes).isNotEmpty()
+
+        val commandBack = SqliteInspectorProtocol.Command.parseFrom(commandBytes)
+        assertThat(commandBack).isEqualTo(command)
+    }
+
+    @Test
+    fun test_basic_inject() {
+        // no crash means the inspector was successfully injected
+        testEnvironment.assertNoQueuedEvents()
+    }
+}
diff --git a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/DatabaseExtensions.kt b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/DatabaseExtensions.kt
new file mode 100644
index 0000000..0d3ea83
--- /dev/null
+++ b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/DatabaseExtensions.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.sqlite.inspection.test
+
+import android.database.sqlite.SQLiteDatabase
+import android.database.sqlite.SQLiteOpenHelper
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.rules.TemporaryFolder
+
+fun SQLiteDatabase.addTable(table: Table) = execSQL(table.toCreateString())
+
+fun SQLiteDatabase.insertValues(table: Table, vararg values: String) {
+    assertThat(values).isNotEmpty()
+    assertThat(values).hasLength(table.columns.size)
+    execSQL(values.joinToString(
+        prefix = "INSERT INTO ${table.name} VALUES(",
+        postfix = ");"
+    ) { it })
+}
+
+fun Database.createInstance(temporaryFolder: TemporaryFolder): SQLiteDatabase {
+    val path = temporaryFolder.newFile(this.name).absolutePath
+    val context = ApplicationProvider.getApplicationContext() as android.content.Context
+    val openHelper = object : SQLiteOpenHelper(context, path, null, 1) {
+        override fun onCreate(db: SQLiteDatabase?) = Unit
+        override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) = Unit
+    }
+    val db = openHelper.readableDatabase
+    tables.forEach { t -> db.addTable(t) }
+    return db
+}
+
+fun Table.toCreateString(): String =
+    columns.joinToString(
+        prefix = "CREATE TABLE $name (",
+        postfix = ");"
+    ) { "${it.name} ${it.type}" }
diff --git a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/GetSchemaTest.kt b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/GetSchemaTest.kt
new file mode 100644
index 0000000..c5b6c2e
--- /dev/null
+++ b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/GetSchemaTest.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.sqlite.inspection.test
+
+import androidx.sqlite.inspection.test.MessageFactory.createGetSchemaCommand
+import androidx.sqlite.inspection.test.MessageFactory.createTrackDatabasesCommand
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@ExperimentalCoroutinesApi
+class GetSchemaTest {
+    @get:Rule
+    val testEnvironment = SqliteInspectorTestEnvironment()
+
+    @get:Rule
+    val temporaryFolder = TemporaryFolder()
+
+    @Test
+    fun test_get_schema_complex_tables() {
+        test_get_schema(
+            listOf(
+                Database(
+                    "db1",
+                    Table(
+                        "table1",
+                        Column("t", "TEXT"),
+                        Column("nu", "NUMERIC"),
+                        Column("i", "INTEGER"),
+                        Column("r", "REAL"),
+                        Column("b", "BLOB")
+                    ),
+                    Table(
+                        "table2",
+                        Column("id", "INTEGER"),
+                        Column("name", "TEXT")
+
+                    )
+                )
+            )
+        )
+    }
+
+    @Test
+    fun test_get_schema_multiple_databases() {
+        test_get_schema(
+            listOf(
+                Database("db3", Table("t3", Column("c3", "BLOB"))),
+                Database("db2", Table("t2", Column("c2", "TEXT"))),
+                Database("db1", Table("t1", Column("c1", "TEXT")))
+            )
+        )
+    }
+
+    private fun test_get_schema(alreadyOpenDatabases: List<Database>) = runBlocking {
+        assertThat(alreadyOpenDatabases).isNotEmpty() // sanity check
+
+        testEnvironment.registerAlreadyOpenDatabases(alreadyOpenDatabases.map {
+            it.createInstance(temporaryFolder)
+        })
+        testEnvironment.sendCommand(createTrackDatabasesCommand())
+        val databaseConnections =
+            alreadyOpenDatabases.indices.map { testEnvironment.receiveEvent().databaseOpened }
+
+        val schemas =
+            databaseConnections
+                .sortedBy { it.name }
+                .map {
+                    testEnvironment.sendCommand(createGetSchemaCommand(it.databaseId)).getSchema
+                }
+
+        alreadyOpenDatabases
+            .sortedBy { it.name }
+            .zipSameSize(schemas)
+            .forEach { (expectedSchema, actualSchema) ->
+                val expectedTables = expectedSchema.tables.sortedBy { it.name }
+                val actualTables = actualSchema.tablesList.sortedBy { it.name }
+
+                expectedTables
+                    .zipSameSize(actualTables)
+                    .forEach { (expectedTable, actualTable) ->
+                        assertThat(actualTable.name).isEqualTo(expectedTable.name)
+
+                        val expectedColumns = expectedTable.columns.sortedBy { it.name }
+                        val actualColumns = actualTable.columnsList.sortedBy { it.name }
+
+                        expectedColumns
+                            .zipSameSize(actualColumns)
+                            .forEach { (expectedColumn, actualColumn) ->
+                                assertThat(actualColumn.name).isEqualTo(expectedColumn.name)
+                                assertThat(actualColumn.type).isEqualTo(expectedColumn.type)
+                            }
+                    }
+            }
+    }
+
+    /** Same as [List.zip] but ensures both lists are the same size. */
+    private fun <A, B> List<A>.zipSameSize(other: List<B>): List<Pair<A, B>> {
+        assertThat(this.size).isEqualTo(other.size)
+        return this.zip(other)
+    }
+}
diff --git a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/Model.kt b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/Model.kt
new file mode 100644
index 0000000..fb9b5052
--- /dev/null
+++ b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/Model.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.sqlite.inspection.test
+
+data class Database(val name: String, val tables: List<Table>) {
+    constructor(name: String, vararg tables: Table) : this(name, tables.toList())
+}
+
+data class Table(val name: String, val columns: List<Column>) {
+    constructor(name: String, vararg columns: Column) : this(name, columns.toList())
+}
+
+data class Column(val name: String, val type: String)
diff --git a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/ProtoExtensions.kt b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/ProtoExtensions.kt
new file mode 100644
index 0000000..2b6c212
--- /dev/null
+++ b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/ProtoExtensions.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.sqlite.inspection.test
+
+import androidx.sqlite.inspection.SqliteInspectorProtocol
+import androidx.sqlite.inspection.SqliteInspectorProtocol.CellValue
+import androidx.sqlite.inspection.SqliteInspectorProtocol.CellValue.ValueCase
+
+val CellValue.value: Any? get() = valueType.first
+val CellValue.type: String get() = valueType.second
+val CellValue.valueType: Pair<Any?, String>
+    get() = when (valueCase) {
+        ValueCase.STRING_VALUE -> stringValue to "text"
+        ValueCase.INT_VALUE -> intValue to "integer"
+        ValueCase.FLOAT_VALUE -> floatValue to "float"
+        ValueCase.BLOB_VALUE -> blobValue.toByteArray().toTypedArray() to "blob"
+        ValueCase.VALUE_NOT_SET -> null to "null"
+        else -> throw IllegalArgumentException()
+    }
+
+object MessageFactory {
+    fun createTrackDatabasesCommand(): SqliteInspectorProtocol.Command =
+        SqliteInspectorProtocol.Command.newBuilder().setTrackDatabases(
+            SqliteInspectorProtocol.TrackDatabasesCommand.getDefaultInstance()
+        ).build()
+
+    fun createTrackDatabasesResponse(): SqliteInspectorProtocol.Response =
+        SqliteInspectorProtocol.Response.newBuilder().setTrackDatabases(
+            SqliteInspectorProtocol.TrackDatabasesResponse.getDefaultInstance()
+        ).build()
+
+    fun createGetSchemaCommand(databaseId: Int): SqliteInspectorProtocol.Command =
+        SqliteInspectorProtocol.Command.newBuilder().setGetSchema(
+            SqliteInspectorProtocol.GetSchemaCommand.newBuilder().setDatabaseId(databaseId).build()
+        ).build()
+
+    fun createQueryTableCommand(
+        databaseId: Int,
+        query: String
+    ): SqliteInspectorProtocol.Command =
+        SqliteInspectorProtocol.Command.newBuilder().setQuery(
+            SqliteInspectorProtocol.QueryCommand.newBuilder()
+                .setDatabaseId(databaseId)
+                .setQuery(query)
+                .build()
+        ).build()
+}
diff --git a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/QueryTest.kt b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/QueryTest.kt
new file mode 100644
index 0000000..917e065
--- /dev/null
+++ b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/QueryTest.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.sqlite.inspection.test
+
+import androidx.sqlite.inspection.SqliteInspectorProtocol
+import androidx.sqlite.inspection.test.MessageFactory.createQueryTableCommand
+import androidx.sqlite.inspection.test.MessageFactory.createTrackDatabasesCommand
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@ExperimentalCoroutinesApi
+// TODO: add tests for invalid queries: union of unequal number of columns, syntax error, etc.
+class QueryTest {
+    @get:Rule
+    val testEnvironment = SqliteInspectorTestEnvironment()
+
+    @get:Rule
+    val temporaryFolder = TemporaryFolder()
+
+    private val table1: Table = Table(
+        "table1",
+        Column("t", "TEXT"),
+        Column("nu", "NUMERIC"),
+        Column("i", "INTEGER"),
+        Column("r", "REAL"),
+        Column("b", "BLOB")
+    )
+
+    private val table2: Table = Table(
+        "table2",
+        Column("id", "INTEGER"),
+        Column("name", "TEXT")
+    )
+
+    private val database = Database("db1", table1, table2)
+
+    /** Query verifying type affinity behaviour */
+    @Test
+    fun test_valid_query_type_affinity_cases() {
+        val values = listOf(
+            table1 to repeat5("'abc'"),
+            table1 to repeat5("'500.0'"),
+            table1 to repeat5("500.0"),
+            table1 to repeat5("500"),
+            table1 to repeat5("x'0500'"),
+            table1 to repeat5("NULL"),
+
+            table2 to arrayOf("1", "'A'"),
+            table2 to arrayOf("2", "'B'"),
+            table2 to arrayOf("3", "'C'"),
+            table2 to arrayOf("4", "'D'")
+        )
+
+        val query = "select * from ${table1.name}"
+
+        val expectedValues = listOf(
+            repeat5("abc").toList(),
+            listOf("500.0", 500, 500, 500.0f, "500.0"), // text|integer|integer|float|text
+            listOf("500.0", 500, 500, 500.0f, 500.0f), // text|integer|integer|float|float
+            listOf("500", 500, 500, 500.0f, 500), // text|integer|integer|float|integer
+            listOf(*repeat5(arrayOf<Any?>(5.toByte(), 0.toByte()))), // blob|blob|blob|blob|blob
+            listOf(*repeat5<Any?>(null)) // null|null|null|null|null
+        )
+
+        val expectedTypes = listOf(
+            repeat5("text").toList(),
+            listOf("text", "integer", "integer", "float", "text"),
+            listOf("text", "integer", "integer", "float", "float"),
+            listOf("text", "integer", "integer", "float", "integer"),
+            listOf("blob", "blob", "blob", "blob", "blob"),
+            listOf("null", "null", "null", "null", "null")
+        )
+
+        val expectedColumnNames = table1.columns.map { it.name }
+
+        test_valid_query(
+            database,
+            values,
+            query,
+            expectedValues,
+            expectedTypes,
+            expectedColumnNames
+        )
+    }
+
+    /** Union of two queries (different column names) resulting in using first query columns. */
+    @Test
+    fun test_valid_query_two_table_union() {
+        val values = listOf(
+            table1 to repeat5("'abc'"),
+            table1 to repeat5("'xyz'"),
+            table2 to arrayOf("1", "'A'"),
+            table2 to arrayOf("2", "'B'")
+        )
+
+        // query construction
+        val columns1 = table1.columns.take(2).map { it.name }
+        val columns2 = table2.columns.take(2).map { it.name }
+        val column11 = columns1.first()
+        val column12 = columns1.last()
+        val column21 = columns2.first()
+        val column22 = columns2.last()
+        val query1 = "select $column11, $column12 from ${table1.name} where $column11 is 'abc'"
+        val query2 = "select $column21, $column22 from ${table2.name} where $column22 is 'B'"
+        val query = "$query1 union $query2"
+
+        val expectedValues = listOf(
+            listOf(2, "B"),
+            listOf("abc", "abc")
+        )
+
+        val expectedTypes = listOf(
+            listOf("integer", "text"),
+            listOf("text", "text")
+        )
+
+        test_valid_query(
+            database,
+            values,
+            query,
+            expectedValues,
+            expectedTypes,
+            expectedColumnNames = columns1
+        )
+    }
+
+    private fun test_valid_query(
+        database: Database,
+        values: List<Pair<Table, Array<String>>>,
+        query: String,
+        expectedValues: List<List<Any?>>,
+        expectedTypes: List<List<String>>,
+        expectedColumnNames: List<String>
+    ) = runBlocking {
+        // given
+        val databaseInstance = database.createInstance(temporaryFolder)
+        values.forEach { (table, values) -> databaseInstance.insertValues(table, *values) }
+
+        testEnvironment.registerAlreadyOpenDatabases(
+            listOf(
+                Database("ignored_1").createInstance(temporaryFolder),
+                databaseInstance,
+                Database("ignored_2").createInstance(temporaryFolder)
+            )
+        )
+        testEnvironment.sendCommand(createTrackDatabasesCommand())
+        val databaseId = testEnvironment.awaitDatabaseOpenedEvent(database.name).databaseId
+
+        // when
+        testEnvironment.sendCommand(createQueryTableCommand(databaseId, query)).let { response ->
+            // then
+            response.query.rowsList.let { actualRows: List<SqliteInspectorProtocol.Row> ->
+                actualRows.forEachIndexed { rowIx, row ->
+                    row.valuesList.forEachIndexed { colIx, cell ->
+                        assertThat(cell.value).isEqualTo(expectedValues[rowIx][colIx])
+                        assertThat(cell.type).isEqualTo(expectedTypes[rowIx][colIx])
+                        assertThat(cell.columnName).isEqualTo(expectedColumnNames[colIx])
+                    }
+                }
+            }
+        }
+    }
+
+    private inline fun <reified T> repeatN(value: T, n: Int): Array<T> =
+        (0 until n).map { value }.toTypedArray()
+
+    private inline fun <reified T> repeat5(v: T) = repeatN(v, 5)
+}
diff --git a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/SqliteInspectorTestEnvironment.kt b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/SqliteInspectorTestEnvironment.kt
new file mode 100644
index 0000000..5c4af60
--- /dev/null
+++ b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/SqliteInspectorTestEnvironment.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.sqlite.inspection.test
+
+import android.database.sqlite.SQLiteDatabase
+import androidx.inspection.InspectorEnvironment
+import androidx.inspection.testing.InspectorTester
+import androidx.sqlite.inspection.SqliteInspectorProtocol.Command
+import androidx.sqlite.inspection.SqliteInspectorProtocol.DatabaseOpenedEvent
+import androidx.sqlite.inspection.SqliteInspectorProtocol.Event
+import androidx.sqlite.inspection.SqliteInspectorProtocol.Response
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
+import org.junit.rules.ExternalResource
+
+private const val SQLITE_INSPECTOR_ID = "androidx.sqlite.inspection"
+
+class SqliteInspectorTestEnvironment : ExternalResource() {
+    private lateinit var inspectorTester: InspectorTester
+    private lateinit var environment: FakeInspectorEnvironment
+
+    override fun before() {
+        environment = FakeInspectorEnvironment()
+        inspectorTester = runBlocking { InspectorTester(SQLITE_INSPECTOR_ID, environment) }
+    }
+
+    override fun after() {
+        inspectorTester.dispose()
+    }
+
+    @ExperimentalCoroutinesApi
+    fun assertNoQueuedEvents() {
+        assertThat(inspectorTester.channel.isEmpty).isTrue()
+    }
+
+    suspend fun sendCommand(command: Command): Response {
+        inspectorTester.sendCommand(command.toByteArray())
+            .let { responseBytes ->
+                assertThat(responseBytes).isNotEmpty()
+                return Response.parseFrom(responseBytes)
+            }
+    }
+
+    suspend fun receiveEvent(): Event {
+        inspectorTester.channel.receive().let { responseBytes ->
+            assertThat(responseBytes).isNotEmpty()
+            return Event.parseFrom(responseBytes)
+        }
+    }
+
+    fun registerAlreadyOpenDatabases(databases: List<SQLiteDatabase>) {
+        environment.registerInstancesToFind(databases)
+    }
+
+    fun consumeRegisteredHooks(): List<Hook> =
+        environment.consumeRegisteredHooks()
+
+    /** Assumes an event with the relevant database will be fired. */
+    suspend fun awaitDatabaseOpenedEvent(databaseName: String): DatabaseOpenedEvent {
+        while (true) {
+            val event = receiveEvent().databaseOpened
+            if (event.name.endsWith("/$databaseName")) {
+                return event
+            }
+        }
+    }
+}
+
+/**
+ * Fake inspector environment with the following behaviour:
+ * - [findInstances] returns pre-registered values from [registerInstancesToFind].
+ * - [registerEntryHook] and [registerExitHook] record the calls which can later be
+ * retrieved in [consumeRegisteredHooks].
+ */
+private class FakeInspectorEnvironment : InspectorEnvironment {
+    private val instancesToFind = mutableListOf<Any>()
+    private val registeredHooks = mutableListOf<Hook>()
+
+    fun registerInstancesToFind(instances: List<Any>) {
+        instancesToFind.addAll(instances)
+    }
+
+    /**
+     *  Returns instances pre-registered in [registerInstancesToFind].
+     *  By design crashes in case of the wrong setup - indicating an issue with test code.
+     */
+    @Suppress("UNCHECKED_CAST")
+    // TODO: implement actual findInstances behaviour
+    override fun <T : Any?> findInstances(clazz: Class<T>): MutableList<T> =
+        instancesToFind.map { it as T }.toMutableList()
+
+    override fun registerEntryHook(
+        originClass: Class<*>,
+        originMethod: String,
+        entryHook: InspectorEnvironment.EntryHook
+    ) {
+        // TODO: implement actual registerEntryHook behaviour
+        registeredHooks.add(Hook.EntryHook(originClass, originMethod, entryHook))
+    }
+
+    override fun <T : Any?> registerExitHook(
+        originClass: Class<*>,
+        originMethod: String,
+        exitHook: InspectorEnvironment.ExitHook<T>
+    ) {
+        // TODO: implement actual registerExitHook behaviour
+        registeredHooks.add(Hook.ExitHook(originClass, originMethod, exitHook))
+    }
+
+    fun consumeRegisteredHooks(): List<Hook> =
+        registeredHooks.toList().also {
+            registeredHooks.clear()
+        }
+}
+
+sealed class Hook(val originClass: Class<*>, val originMethod: String) {
+    class ExitHook(
+        originClass: Class<*>,
+        originMethod: String,
+        val exitHook: InspectorEnvironment.ExitHook<*>
+    ) : Hook(originClass, originMethod)
+
+    class EntryHook(
+        originClass: Class<*>,
+        originMethod: String,
+        @Suppress("unused") val entryHook: InspectorEnvironment.EntryHook
+    ) : Hook(originClass, originMethod)
+}
diff --git a/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/TrackDatabasesTest.kt b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/TrackDatabasesTest.kt
new file mode 100644
index 0000000..560b76e
--- /dev/null
+++ b/sqlite/sqlite-inspection/src/androidTest/java/androidx/sqlite/inspection/test/TrackDatabasesTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.sqlite.inspection.test
+
+import android.database.sqlite.SQLiteDatabase
+import androidx.inspection.InspectorEnvironment
+import androidx.sqlite.inspection.test.MessageFactory.createTrackDatabasesCommand
+import androidx.sqlite.inspection.test.MessageFactory.createTrackDatabasesResponse
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+
+private const val OPEN_DATABASE_COMMAND_SIGNATURE: String = "openDatabase" +
+        "(" +
+        "Ljava/io/File;" +
+        "Landroid/database/sqlite/SQLiteDatabase\$OpenParams;" +
+        ")" +
+        "Landroid/database/sqlite/SQLiteDatabase;"
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@ExperimentalCoroutinesApi
+class TrackDatabasesTest {
+    @get:Rule
+    val testEnvironment = SqliteInspectorTestEnvironment()
+
+    @get:Rule
+    val temporaryFolder = TemporaryFolder()
+
+    @Test
+    fun test_track_databases() = runBlocking {
+        val alreadyOpenDatabases = listOf(
+            Database("db1").createInstance(temporaryFolder),
+            Database("db2").createInstance(temporaryFolder)
+        )
+
+        testEnvironment.registerAlreadyOpenDatabases(alreadyOpenDatabases)
+
+        testEnvironment.sendCommand(createTrackDatabasesCommand()).let { response ->
+            assertThat(response).isEqualTo(createTrackDatabasesResponse())
+        }
+
+        // evaluate 'already-open' instances are found
+        alreadyOpenDatabases.let { expected ->
+            val actual = expected.indices.map { testEnvironment.receiveEvent().databaseOpened }
+            testEnvironment.assertNoQueuedEvents()
+            assertThat(actual.map { it.databaseId }.distinct()).hasSize(expected.size)
+            expected.forEachIndexed { ix, _ ->
+                assertThat(actual[ix].name).isEqualTo(expected[ix].path)
+            }
+        }
+
+        // evaluate registered hooks
+        val hookEntries = testEnvironment.consumeRegisteredHooks()
+        assertThat(hookEntries).hasSize(1)
+        hookEntries.first().let { entry ->
+            // expect one exit hook tracking database open events
+            assertThat(entry).isInstanceOf(Hook.ExitHook::class.java)
+            assertThat(entry.originClass.name).isEqualTo(SQLiteDatabase::class.java.name)
+            assertThat(entry.originMethod)
+                .isEqualTo(OPEN_DATABASE_COMMAND_SIGNATURE)
+
+            // verify that executing the registered hook will result in tracking events
+            testEnvironment.assertNoQueuedEvents()
+            @Suppress("UNCHECKED_CAST")
+            val exitHook = (entry as Hook.ExitHook).exitHook as
+                    InspectorEnvironment.ExitHook<SQLiteDatabase>
+            val database = Database("db3").createInstance(temporaryFolder)
+            assertThat(exitHook.onExit(database)).isSameInstanceAs(database)
+            testEnvironment.receiveEvent().let { event ->
+                assertThat(event.databaseOpened.name).isEqualTo(database.path)
+            }
+        }
+
+        assertThat(testEnvironment.consumeRegisteredHooks()).isEmpty()
+    }
+}
diff --git a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspector.java b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspector.java
index 451c33f..86fd084 100644
--- a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspector.java
+++ b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspector.java
@@ -22,7 +22,6 @@
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
 import androidx.inspection.Connection;
 import androidx.inspection.Inspector;
 import androidx.inspection.InspectorEnvironment;
@@ -60,8 +59,7 @@
  */
 final class SqliteInspector extends Inspector {
     // TODO: identify all SQLiteDatabase openDatabase methods
-    @SuppressWarnings("WeakerAccess")
-    @VisibleForTesting static final String sOpenDatabaseCommandSignature = "openDatabase"
+    private static final String sOpenDatabaseCommandSignature = "openDatabase"
             + "("
             + "Ljava/io/File;"
             + "Landroid/database/sqlite/SQLiteDatabase$OpenParams;"
diff --git a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspectorFactory.java b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspectorFactory.java
index 4e28dd9..6f37957 100644
--- a/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspectorFactory.java
+++ b/sqlite/sqlite-inspection/src/main/java/androidx/sqlite/inspection/SqliteInspectorFactory.java
@@ -17,7 +17,6 @@
 package androidx.sqlite.inspection;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
 import androidx.inspection.Connection;
 import androidx.inspection.InspectorEnvironment;
 import androidx.inspection.InspectorFactory;
@@ -26,7 +25,7 @@
  * Factory for SqliteInspector
  */
 public final class SqliteInspectorFactory extends InspectorFactory<SqliteInspector> {
-    @VisibleForTesting static final String SQLITE_INSPECTOR_ID = "androidx.sqlite.inspection";
+    private static final String SQLITE_INSPECTOR_ID = "androidx.sqlite.inspection";
 
     public SqliteInspectorFactory() {
         super(SQLITE_INSPECTOR_ID);
diff --git a/sqlite/sqlite/src/main/java/androidx/sqlite/db/SupportSQLiteOpenHelper.java b/sqlite/sqlite/src/main/java/androidx/sqlite/db/SupportSQLiteOpenHelper.java
index afe705f..f39b07c 100644
--- a/sqlite/sqlite/src/main/java/androidx/sqlite/db/SupportSQLiteOpenHelper.java
+++ b/sqlite/sqlite/src/main/java/androidx/sqlite/db/SupportSQLiteOpenHelper.java
@@ -358,7 +358,7 @@
             Context mContext;
             String mName;
             SupportSQLiteOpenHelper.Callback mCallback;
-            boolean mUseNoBackUpDirectory;
+            boolean mUseNoBackupDirectory;
 
             /**
              * <p>
@@ -381,12 +381,12 @@
                     throw new IllegalArgumentException("Must set a non-null context to create"
                             + " the configuration.");
                 }
-                if (mUseNoBackUpDirectory && TextUtils.isEmpty(mName)) {
+                if (mUseNoBackupDirectory && TextUtils.isEmpty(mName)) {
                     throw new IllegalArgumentException(
                             "Must set a non-null database name to a configuration that uses the "
                                     + "no backup directory.");
                 }
-                return new Configuration(mContext, mName, mCallback, mUseNoBackUpDirectory);
+                return new Configuration(mContext, mName, mCallback, mUseNoBackupDirectory);
             }
 
             Builder(@NonNull Context context) {
@@ -415,13 +415,13 @@
 
             /**
              * Sets whether to use a no backup directory or not.
-             * @param useNoBackUpDirectory If {@code true} the database file will be stored in the
+             * @param useNoBackupDirectory If {@code true} the database file will be stored in the
              *                             no-backup directory.
              * @return this
              */
             @NonNull
-            public Builder noBackupDirectory(boolean useNoBackUpDirectory) {
-                mUseNoBackUpDirectory = useNoBackUpDirectory;
+            public Builder noBackupDirectory(boolean useNoBackupDirectory) {
+                mUseNoBackupDirectory = useNoBackupDirectory;
                 return this;
             }
         }
diff --git a/testutils/testutils-gradle-plugin/build.gradle b/testutils/testutils-gradle-plugin/build.gradle
new file mode 100644
index 0000000..43650cb
--- /dev/null
+++ b/testutils/testutils-gradle-plugin/build.gradle
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("kotlin")
+}
+
+dependencies {
+    implementation(KOTLIN_STDLIB)
+    implementation(ANDROIDX_TEST_EXT_JUNIT)
+    implementation(ANDROIDX_TEST_CORE)
+    implementation(ANDROIDX_TEST_RULES)
+}
+
+androidx {
+    toolingProject = true
+}
diff --git a/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt b/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt
new file mode 100644
index 0000000..d492dbb
--- /dev/null
+++ b/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.gradle
+
+import org.junit.rules.ExternalResource
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import java.io.File
+import java.lang.IllegalStateException
+import java.util.Properties
+
+/**
+ * Test rule that helps to setup android project in tests that run gradle.
+ *
+ * It should be used along side with SdkResourceGenerator in your build.gradle file
+ */
+class ProjectSetupRule : ExternalResource() {
+    val testProjectDir = TemporaryFolder()
+
+    lateinit var props: ProjectProps
+
+    val rootDir: File
+        get() = testProjectDir.root
+
+    val buildFile: File
+        get() = File(rootDir, "build.gradle")
+
+    val gradlePropertiesFile: File
+        get() = File(rootDir, "gradle.properties")
+
+    private val repositories: String
+        get() = """
+            repositories {
+                maven { url "${props.prebuiltsRoot}/androidx/external" }
+                maven { url "${props.prebuiltsRoot}/androidx/internal" }
+            }
+        """.trimIndent()
+
+    val androidProject: String
+        get() = """
+            android {
+                compileSdkVersion ${props.compileSdkVersion}
+                buildToolsVersion "${props.buildToolsVersion}"
+
+                defaultConfig {
+                    minSdkVersion ${props.minSdkVersion}
+                }
+
+                signingConfigs {
+                    debug {
+                        storeFile file("${props.debugKeystore}")
+                    }
+                }
+            }
+        """.trimIndent()
+
+    private val defaultBuildGradle: String
+        get() = "\n$repositories\n\n$androidProject\n\n"
+
+    fun writeDefaultBuildGradle(prefix: String, suffix: String) {
+        buildFile.writeText(prefix)
+        buildFile.appendText(defaultBuildGradle)
+        buildFile.appendText(suffix)
+
+        println(buildFile.readText())
+    }
+
+    override fun apply(base: Statement, description: Description): Statement {
+        return testProjectDir.apply(super.apply(base, description), description)
+    }
+
+    override fun before() {
+        props = ProjectProps.load()
+        buildFile.createNewFile()
+        copyLocalProperties()
+        writeGradleProperties()
+    }
+
+    private fun copyLocalProperties() {
+        val localProperties = File(props.rootProjectPath, "local.properties")
+        if (localProperties.exists()) {
+            localProperties.copyTo(File(rootDir, "local.properties"), overwrite = true)
+        } else {
+            throw IllegalStateException("local.properties doesn't exist at: $localProperties")
+        }
+    }
+
+    private fun writeGradleProperties() {
+        gradlePropertiesFile.writer().use {
+            val props = Properties()
+            props.setProperty("android.useAndroidX", "true")
+            props.store(it, null)
+        }
+    }
+}
+
+data class ProjectProps(
+    val prebuiltsRoot: String,
+    val compileSdkVersion: String,
+    val buildToolsVersion: String,
+    val minSdkVersion: String,
+    val debugKeystore: String,
+    var navigationCommon: String,
+    val kotlinStblib: String,
+    val rootProjectPath: String,
+    val localSupportRepo: String,
+    val agpDependency: String
+) {
+    companion object {
+        fun load(): ProjectProps {
+            val stream = ProjectSetupRule::class.java.classLoader.getResourceAsStream("sdk.prop")
+            val properties = Properties()
+            properties.load(stream)
+            return ProjectProps(
+                prebuiltsRoot = properties.getProperty("prebuiltsRoot"),
+                compileSdkVersion = properties.getProperty("compileSdkVersion"),
+                buildToolsVersion = properties.getProperty("buildToolsVersion"),
+                minSdkVersion = properties.getProperty("minSdkVersion"),
+                debugKeystore = properties.getProperty("debugKeystore"),
+                navigationCommon = properties.getProperty("navigationCommon"),
+                kotlinStblib = properties.getProperty("kotlinStdlib"),
+                rootProjectPath = properties.getProperty("rootProjectPath"),
+                localSupportRepo = properties.getProperty("localSupportRepo"),
+                agpDependency = properties.getProperty("agpDependency")
+            )
+        }
+    }
+}
diff --git a/ui/integration-tests/test/src/androidTest/java/androidx/ui/integration/test/VectorAssetTest.kt b/ui/integration-tests/test/src/androidTest/java/androidx/ui/integration/test/VectorAssetTest.kt
index 600acd7..db585d6 100644
--- a/ui/integration-tests/test/src/androidTest/java/androidx/ui/integration/test/VectorAssetTest.kt
+++ b/ui/integration-tests/test/src/androidTest/java/androidx/ui/integration/test/VectorAssetTest.kt
@@ -16,7 +16,9 @@
 
 package androidx.ui.integration.test
 
+import android.os.Build
 import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
 import androidx.ui.layout.Column
 import androidx.ui.test.captureToBitmap
 import androidx.ui.integration.test.framework.ProgrammaticVectorTestCase
@@ -35,6 +37,7 @@
  * output when ran. This ensures correctness for the corresponding benchmarks.
  */
 @LargeTest
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
 @RunWith(JUnit4::class)
 class VectorAssetTest {
     @get:Rule
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/integration/test/framework/VectorAssetTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/integration/test/framework/VectorAssetTestCase.kt
index 3ab13ab..dda850b 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/integration/test/framework/VectorAssetTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/integration/test/framework/VectorAssetTestCase.kt
@@ -19,7 +19,6 @@
 import androidx.compose.Composable
 import androidx.ui.core.TestTag
 import androidx.ui.core.dp
-import androidx.ui.core.px
 import androidx.ui.graphics.Color
 import androidx.ui.graphics.SolidColor
 import androidx.ui.graphics.StrokeCap
@@ -82,8 +81,8 @@
      */
     @Composable
     override fun getVectorAsset() = VectorAssetBuilder(
-        defaultWidth = 84.px,
-        defaultHeight = 84.px,
+        defaultWidth = 24.dp,
+        defaultHeight = 24.dp,
         viewportWidth = 24f,
         viewportHeight = 24f
     ).apply {
diff --git a/ui/ui-android-view-non-ir/src/main/java/androidx/ui/androidview/adapters/ViewAttributeAdapter.kt b/ui/ui-android-view-non-ir/src/main/java/androidx/ui/androidview/adapters/ViewAttributeAdapter.kt
index 8f671fd..1b77435 100644
--- a/ui/ui-android-view-non-ir/src/main/java/androidx/ui/androidview/adapters/ViewAttributeAdapter.kt
+++ b/ui/ui-android-view-non-ir/src/main/java/androidx/ui/androidview/adapters/ViewAttributeAdapter.kt
@@ -18,9 +18,8 @@
 
 package androidx.ui.androidview.adapters
 
-import android.annotation.SuppressLint
-import androidx.annotation.DimenRes
 import android.view.View
+import androidx.annotation.DimenRes
 import androidx.ui.androidview.annotations.ConflictsWith
 
 // NOTE: these attributes are added to every view/component so that we can have a "key" attribute
@@ -146,7 +145,6 @@
 fun View.setScrollY(scrollY: Dimension) = setScrollY(scrollY.toIntPixels(metrics))
 fun View.setTop(top: Dimension) = setTop(top.toIntPixels(metrics))
 
-@SuppressLint("UnnecessaryLambdaCreation")
 // TODO: Necessary because the IR doesn't support SAM conversion yet
 fun View.setOnClick(lambda: () -> Unit) { this.setOnClickListener { lambda() } }
 
diff --git a/ui/ui-core/api/0.1.0-dev04.txt b/ui/ui-core/api/0.1.0-dev04.txt
index 736a077..6cad729 100644
--- a/ui/ui-core/api/0.1.0-dev04.txt
+++ b/ui/ui-core/api/0.1.0-dev04.txt
@@ -524,7 +524,7 @@
     enum_constant public static final androidx.ui.core.LayoutDirection Rtl;
   }
 
-  public interface LayoutModifier extends androidx.ui.core.ParentDataModifier {
+  public interface LayoutModifier extends androidx.ui.core.Modifier.Element {
     method public default androidx.ui.core.IntPx maxIntrinsicHeightOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx width);
     method public default androidx.ui.core.IntPx maxIntrinsicWidthOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx height);
     method public default androidx.ui.core.IntPx minIntrinsicHeightOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx width);
diff --git a/ui/ui-core/api/current.txt b/ui/ui-core/api/current.txt
index 736a077..6cad729 100644
--- a/ui/ui-core/api/current.txt
+++ b/ui/ui-core/api/current.txt
@@ -524,7 +524,7 @@
     enum_constant public static final androidx.ui.core.LayoutDirection Rtl;
   }
 
-  public interface LayoutModifier extends androidx.ui.core.ParentDataModifier {
+  public interface LayoutModifier extends androidx.ui.core.Modifier.Element {
     method public default androidx.ui.core.IntPx maxIntrinsicHeightOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx width);
     method public default androidx.ui.core.IntPx maxIntrinsicWidthOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx height);
     method public default androidx.ui.core.IntPx minIntrinsicHeightOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx width);
diff --git a/ui/ui-core/api/public_plus_experimental_0.1.0-dev04.txt b/ui/ui-core/api/public_plus_experimental_0.1.0-dev04.txt
index 736a077..6cad729 100644
--- a/ui/ui-core/api/public_plus_experimental_0.1.0-dev04.txt
+++ b/ui/ui-core/api/public_plus_experimental_0.1.0-dev04.txt
@@ -524,7 +524,7 @@
     enum_constant public static final androidx.ui.core.LayoutDirection Rtl;
   }
 
-  public interface LayoutModifier extends androidx.ui.core.ParentDataModifier {
+  public interface LayoutModifier extends androidx.ui.core.Modifier.Element {
     method public default androidx.ui.core.IntPx maxIntrinsicHeightOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx width);
     method public default androidx.ui.core.IntPx maxIntrinsicWidthOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx height);
     method public default androidx.ui.core.IntPx minIntrinsicHeightOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx width);
diff --git a/ui/ui-core/api/public_plus_experimental_current.txt b/ui/ui-core/api/public_plus_experimental_current.txt
index 736a077..6cad729 100644
--- a/ui/ui-core/api/public_plus_experimental_current.txt
+++ b/ui/ui-core/api/public_plus_experimental_current.txt
@@ -524,7 +524,7 @@
     enum_constant public static final androidx.ui.core.LayoutDirection Rtl;
   }
 
-  public interface LayoutModifier extends androidx.ui.core.ParentDataModifier {
+  public interface LayoutModifier extends androidx.ui.core.Modifier.Element {
     method public default androidx.ui.core.IntPx maxIntrinsicHeightOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx width);
     method public default androidx.ui.core.IntPx maxIntrinsicWidthOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx height);
     method public default androidx.ui.core.IntPx minIntrinsicHeightOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx width);
diff --git a/ui/ui-core/api/restricted_0.1.0-dev04.txt b/ui/ui-core/api/restricted_0.1.0-dev04.txt
index 736a077..6cad729 100644
--- a/ui/ui-core/api/restricted_0.1.0-dev04.txt
+++ b/ui/ui-core/api/restricted_0.1.0-dev04.txt
@@ -524,7 +524,7 @@
     enum_constant public static final androidx.ui.core.LayoutDirection Rtl;
   }
 
-  public interface LayoutModifier extends androidx.ui.core.ParentDataModifier {
+  public interface LayoutModifier extends androidx.ui.core.Modifier.Element {
     method public default androidx.ui.core.IntPx maxIntrinsicHeightOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx width);
     method public default androidx.ui.core.IntPx maxIntrinsicWidthOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx height);
     method public default androidx.ui.core.IntPx minIntrinsicHeightOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx width);
diff --git a/ui/ui-core/api/restricted_current.txt b/ui/ui-core/api/restricted_current.txt
index 736a077..6cad729 100644
--- a/ui/ui-core/api/restricted_current.txt
+++ b/ui/ui-core/api/restricted_current.txt
@@ -524,7 +524,7 @@
     enum_constant public static final androidx.ui.core.LayoutDirection Rtl;
   }
 
-  public interface LayoutModifier extends androidx.ui.core.ParentDataModifier {
+  public interface LayoutModifier extends androidx.ui.core.Modifier.Element {
     method public default androidx.ui.core.IntPx maxIntrinsicHeightOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx width);
     method public default androidx.ui.core.IntPx maxIntrinsicWidthOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx height);
     method public default androidx.ui.core.IntPx minIntrinsicHeightOf(androidx.ui.core.DensityScope, androidx.ui.core.Measurable measurable, androidx.ui.core.IntPx width);
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/LayoutModifier.kt b/ui/ui-core/src/main/java/androidx/ui/core/LayoutModifier.kt
index 33fef24..0522f21 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/LayoutModifier.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/LayoutModifier.kt
@@ -19,7 +19,7 @@
 /**
  * A [Modifier.Element] that changes the way a UI component is measured and laid out.
  */
-interface LayoutModifier : ParentDataModifier {
+interface LayoutModifier : Modifier.Element {
     /**
      * Modifies [constraints] for performing measurement of the modified layout element.
      */
diff --git a/ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/ScrollerTest.kt b/ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/ScrollerTest.kt
index 74e8e03..d3acedf 100644
--- a/ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/ScrollerTest.kt
+++ b/ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/ScrollerTest.kt
@@ -15,29 +15,22 @@
  */
 package androidx.ui.foundation
 
-import android.graphics.Bitmap
 import android.os.Handler
 import android.os.Looper
-import android.view.PixelCopy
-import android.view.View
-import android.view.ViewGroup
-import android.view.ViewTreeObserver
 import androidx.annotation.RequiresApi
 import androidx.compose.Composable
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
 import androidx.ui.core.Alignment
-import androidx.ui.core.AndroidComposeView
 import androidx.ui.core.Dp
 import androidx.ui.core.Draw
 import androidx.ui.core.IntPx
-import androidx.ui.core.Px
+import androidx.ui.core.IntPxSize
 import androidx.ui.core.TestTag
 import androidx.ui.core.Text
 import androidx.ui.core.dp
 import androidx.ui.core.ipx
 import androidx.ui.core.px
-import androidx.ui.core.setContent
 import androidx.ui.core.toPx
 import androidx.ui.core.toRect
 import androidx.ui.core.withDensity
@@ -46,7 +39,6 @@
 import androidx.ui.graphics.Color
 import androidx.ui.graphics.Paint
 import androidx.ui.graphics.PaintingStyle
-import androidx.ui.graphics.toArgb
 import androidx.ui.layout.Align
 import androidx.ui.layout.Column
 import androidx.ui.layout.Container
@@ -55,9 +47,10 @@
 import androidx.ui.semantics.Semantics
 import androidx.ui.test.GestureScope
 import androidx.ui.test.SemanticsNodeInteraction
-import androidx.ui.test.android.AndroidComposeTestRule
 import androidx.ui.test.assertIsDisplayed
 import androidx.ui.test.assertIsNotDisplayed
+import androidx.ui.test.assertPixels
+import androidx.ui.test.captureToBitmap
 import androidx.ui.test.createComposeRule
 import androidx.ui.test.doGesture
 import androidx.ui.test.doScrollTo
@@ -70,13 +63,11 @@
 import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
-import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
 
 @SmallTest
 @RunWith(JUnit4::class)
@@ -85,10 +76,7 @@
     @get:Rule
     val composeTestRule = createComposeRule()
 
-    // TODO(malkov/pavlis) : some tests here require activity access as we need
-    // to take screen's bitmap, abstract it better
-    private val activity
-        get() = (composeTestRule as AndroidComposeTestRule).activityTestRule.activity
+    private val scrollerTag = "ScrollerTest"
 
     private val defaultCrossAxisSize = 45.ipx
     private val defaultMainAxisSize = 40.ipx
@@ -105,17 +93,6 @@
         Color(red = 0xA5, green = 0, blue = 0xFF, alpha = 0xFF)
     )
 
-    private var drawLatch = CountDownLatch(1)
-    private lateinit var handler: Handler
-
-    @Before
-    fun setupDrawLatch() {
-        drawLatch = CountDownLatch(1)
-        composeTestRule.runOnUiThread {
-            handler = Handler()
-        }
-    }
-
     @SdkSuppress(minSdkVersion = 26)
     @Test
     fun verticalScroller_SmallContent() {
@@ -276,31 +253,27 @@
         val scrollerPosition = ScrollerPosition()
 
         createScrollableContent(isVertical, scrollerPosition = scrollerPosition)
-        assertThat(scrollerPosition.getValueOnUiThread()).isEqualTo(0.px)
 
-        findByTag("scroller")
+        composeTestRule.runOnIdleCompose {
+            assertThat(scrollerPosition.value).isEqualTo(0.px)
+        }
+
+        findByTag(scrollerTag)
             .doGesture { firstSwipe() }
             .awaitScrollAnimation(scrollerPosition)
 
-        val scrolledValue = scrollerPosition.getValueOnUiThread()
+        val scrolledValue = composeTestRule.runOnIdleCompose {
+            scrollerPosition.value
+        }
         assertThat(scrolledValue).isGreaterThan(0.px)
 
-        findByTag("scroller")
+        findByTag(scrollerTag)
             .doGesture { secondSwipe() }
             .awaitScrollAnimation(scrollerPosition)
 
-        assertThat(scrollerPosition.getValueOnUiThread()).isLessThan(scrolledValue)
-    }
-
-    private fun ScrollerPosition.getValueOnUiThread(): Px {
-        var value = 0.px
-        val latch = CountDownLatch(1)
-        composeTestRule.runOnUiThread {
-            value = this.value
-            latch.countDown()
+        composeTestRule.runOnIdleCompose {
+            assertThat(scrollerPosition.value).isLessThan(scrolledValue)
         }
-        latch.await()
-        return value
     }
 
     private fun composeVerticalScroller(
@@ -311,9 +284,9 @@
     ) {
         // We assume that the height of the device is more than 45 px
         withDensity(composeTestRule.density) {
-            composeTestRule.runOnUiThread {
-                activity.setContent {
-                    Align(alignment = Alignment.TopLeft) {
+            composeTestRule.setContent {
+                Align(alignment = Alignment.TopLeft) {
+                    TestTag(scrollerTag) {
                         VerticalScroller(
                             scrollerPosition = scrollerPosition,
                             modifier = LayoutSize(width.toDp(), height.toDp())
@@ -334,9 +307,6 @@
                                 }
                             }
                         }
-                        Draw { _, _ ->
-                            drawLatch.countDown()
-                        }
                     }
                 }
             }
@@ -351,9 +321,9 @@
     ) {
         // We assume that the height of the device is more than 45 px
         withDensity(composeTestRule.density) {
-            composeTestRule.runOnUiThread {
-                activity.setContent {
-                    Align(alignment = Alignment.TopLeft) {
+            composeTestRule.setContent {
+                Align(alignment = Alignment.TopLeft) {
+                    TestTag(scrollerTag) {
                         HorizontalScroller(
                             scrollerPosition = scrollerPosition,
                             modifier = LayoutSize(width.toDp(), height.toDp())
@@ -374,9 +344,6 @@
                                 }
                             }
                         }
-                        Draw { _, _ ->
-                            drawLatch.countDown()
-                        }
                     }
                 }
             }
@@ -390,23 +357,12 @@
         height: IntPx = 40.ipx,
         rowHeight: IntPx = 5.ipx
     ) {
-        assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
-
-        val bitmap = waitAndScreenShot()
-        assertTrue(bitmap.height >= height.value)
-        assertTrue(bitmap.width >= width.value)
-        for (y in 0 until height.value) {
-            val colorIndex = (offset.value + y) / rowHeight.value
-            val expectedColor = colors[colorIndex]
-
-            for (x in 0 until width.value) {
-                val pixel = bitmap.getPixel(x, y)
-                assertEquals(
-                    "Expected $expectedColor, but got ${Color(pixel)} at $x, $y",
-                    expectedColor.toArgb(), pixel
-                )
+        findByTag(scrollerTag)
+            .captureToBitmap()
+            .assertPixels(expectedSize = IntPxSize(width, height)) { pos ->
+                val colorIndex = (offset.value + pos.y.value) / rowHeight.value
+                colors[colorIndex]
             }
-        }
     }
 
     @RequiresApi(api = 26)
@@ -416,23 +372,12 @@
         height: IntPx = 45.ipx,
         columnWidth: IntPx = 5.ipx
     ) {
-        assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
-
-        val bitmap = waitAndScreenShot()
-        assertTrue(bitmap.height >= height.value)
-        assertTrue(bitmap.width >= width.value)
-        for (x in 0 until width.value) {
-            val colorIndex = (offset.value + x) / columnWidth.value
-            val expectedColor = colors[colorIndex]
-
-            for (y in 0 until height.value) {
-                val pixel = bitmap.getPixel(x, y)
-                assertEquals(
-                    "Expected $expectedColor, but got ${Color(pixel)} at $x, $y",
-                    expectedColor.toArgb(), pixel
-                )
+        findByTag(scrollerTag)
+            .captureToBitmap()
+            .assertPixels(expectedSize = IntPxSize(width, height)) { pos ->
+                val colorIndex = (offset.value + pos.x.value) / columnWidth.value
+                colors[colorIndex]
             }
-        }
     }
 
     private fun createScrollableContent(
@@ -451,55 +396,29 @@
             Align(alignment = Alignment.TopLeft) {
                 Container(width = width, height = height) {
                     DrawShape(RectangleShape, Color.White)
-                    TestTag("scroller") {
-                        Semantics {
-                            if (isVertical) {
-                                VerticalScroller(scrollerPosition) {
-                                    Column {
-                                        content()
+                        TestTag(scrollerTag) {
+                            Semantics {
+                                if (isVertical) {
+                                    VerticalScroller(scrollerPosition) {
+                                        Column {
+                                            content()
+                                        }
                                     }
-                                }
-                            } else {
-                                HorizontalScroller(scrollerPosition) {
-                                    Row {
-                                        content()
+                                } else {
+                                    HorizontalScroller(scrollerPosition) {
+                                        Row {
+                                            content()
+                                        }
                                     }
                                 }
                             }
                         }
-                    }
                 }
             }
         }
     }
 
-    @RequiresApi(api = 26) // For PixelCopy.request(Window, Rect, Bitmap, listener, Handler)
-    private fun waitAndScreenShot(): Bitmap {
-        val view = findAndroidComposeView()
-        waitForDraw(view)
-
-        val offset = intArrayOf(0, 0)
-        val width = view.width
-        val height = view.height
-
-        val dest =
-            Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
-        val srcRect = android.graphics.Rect(0, 0, width, height)
-        srcRect.offset(offset[0], offset[1])
-        val latch = CountDownLatch(1)
-        var copyResult = 0
-        val onCopyFinished = object : PixelCopy.OnPixelCopyFinishedListener {
-            override fun onPixelCopyFinished(result: Int) {
-                copyResult = result
-                latch.countDown()
-            }
-        }
-        PixelCopy.request(activity.window, srcRect, dest, onCopyFinished, handler)
-        assertTrue(latch.await(1, TimeUnit.SECONDS))
-        assertEquals(PixelCopy.SUCCESS, copyResult)
-        return dest
-    }
-
+    // TODO(b/147291885): This should not be needed in the future.
     private fun SemanticsNodeInteraction.awaitScrollAnimation(
         scroller: ScrollerPosition
     ): SemanticsNodeInteraction {
@@ -520,42 +439,4 @@
         latch.await()
         return this
     }
-
-    // TODO(malkov): ALL below is copypaste from LayoutTest as this test in ui-foundation now
-
-    private fun findAndroidComposeView(): AndroidComposeView {
-        val contentViewGroup = activity.findViewById<ViewGroup>(android.R.id.content)
-        return findAndroidComposeView(contentViewGroup)!!
-    }
-
-    private fun findAndroidComposeView(parent: ViewGroup): AndroidComposeView? {
-        for (index in 0 until parent.childCount) {
-            val child = parent.getChildAt(index)
-            if (child is AndroidComposeView) {
-                return child
-            } else if (child is ViewGroup) {
-                val composeView = findAndroidComposeView(child)
-                if (composeView != null) {
-                    return composeView
-                }
-            }
-        }
-        return null
-    }
-
-    private fun waitForDraw(view: View) {
-        val viewDrawLatch = CountDownLatch(1)
-        val listener = object : ViewTreeObserver.OnDrawListener {
-            override fun onDraw() {
-                viewDrawLatch.countDown()
-            }
-        }
-        view.post(object : Runnable {
-            override fun run() {
-                view.viewTreeObserver.addOnDrawListener(listener)
-                view.invalidate()
-            }
-        })
-        assertTrue(viewDrawLatch.await(1, TimeUnit.SECONDS))
-    }
 }
diff --git a/ui/ui-framework/api/0.1.0-dev04.txt b/ui/ui-framework/api/0.1.0-dev04.txt
index 2086670..d14369a 100644
--- a/ui/ui-framework/api/0.1.0-dev04.txt
+++ b/ui/ui-framework/api/0.1.0-dev04.txt
@@ -394,8 +394,15 @@
 package androidx.ui.graphics.vector {
 
   public final class VectorAsset {
-    method public androidx.ui.core.Px getDefaultHeight();
-    method public androidx.ui.core.Px getDefaultWidth();
+    method public String component1();
+    method public androidx.ui.core.Dp component2();
+    method public androidx.ui.core.Dp component3();
+    method public float component4();
+    method public float component5();
+    method public androidx.ui.graphics.vector.VectorGroup component6();
+    method public androidx.ui.graphics.vector.VectorAsset copy(String name, androidx.ui.core.Dp defaultWidth, androidx.ui.core.Dp defaultHeight, float viewportWidth, float viewportHeight, androidx.ui.graphics.vector.VectorGroup root);
+    method public androidx.ui.core.Dp getDefaultHeight();
+    method public androidx.ui.core.Dp getDefaultWidth();
     method public String getName();
     method public androidx.ui.graphics.vector.VectorGroup getRoot();
     method public float getViewportHeight();
@@ -403,12 +410,12 @@
   }
 
   public final class VectorAssetBuilder {
-    ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+    ctor public VectorAssetBuilder(String name, androidx.ui.core.Dp defaultWidth, androidx.ui.core.Dp defaultHeight, float viewportWidth, float viewportHeight);
     method public androidx.ui.graphics.vector.VectorAssetBuilder addPath(java.util.List<androidx.ui.graphics.vector.PathNode> pathData, String name = "", androidx.ui.graphics.Brush? fill = null, float fillAlpha = 1.0f, androidx.ui.graphics.Brush? stroke = null, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.graphics.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
     method public androidx.ui.graphics.vector.VectorAsset build();
     method public void ensureNotConsumed();
-    method public androidx.ui.core.Px getDefaultHeight();
-    method public androidx.ui.core.Px getDefaultWidth();
+    method public androidx.ui.core.Dp getDefaultHeight();
+    method public androidx.ui.core.Dp getDefaultWidth();
     method public String getName();
     method public float getViewportHeight();
     method public float getViewportWidth();
@@ -430,6 +437,16 @@
   public final class VectorGroup extends androidx.ui.graphics.vector.VectorNode implements java.lang.Iterable<androidx.ui.graphics.vector.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
     ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, java.util.List<androidx.ui.graphics.vector.PathNode> clipPathData);
     ctor public VectorGroup();
+    method public String component1();
+    method public float component2();
+    method public float component3();
+    method public float component4();
+    method public float component5();
+    method public float component6();
+    method public float component7();
+    method public float component8();
+    method public java.util.List<androidx.ui.graphics.vector.PathNode> component9();
+    method public androidx.ui.graphics.vector.VectorGroup copy(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, java.util.List<androidx.ui.graphics.vector.PathNode> clipPathData);
     method public operator androidx.ui.graphics.vector.VectorNode get(int index);
     method public java.util.List<androidx.ui.graphics.vector.PathNode> getClipPathData();
     method public String getName();
@@ -450,6 +467,17 @@
 
   public final class VectorPath extends androidx.ui.graphics.vector.VectorNode {
     ctor public VectorPath(String name, java.util.List<androidx.ui.graphics.vector.PathNode> pathData, androidx.ui.graphics.Brush? fill, float fillAlpha, androidx.ui.graphics.Brush? stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.graphics.StrokeCap strokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin, float strokeLineMiter);
+    method public String component1();
+    method public float component10();
+    method public java.util.List<androidx.ui.graphics.vector.PathNode> component2();
+    method public androidx.ui.graphics.Brush? component3();
+    method public float component4();
+    method public androidx.ui.graphics.Brush? component5();
+    method public float component6();
+    method public float component7();
+    method public androidx.ui.graphics.StrokeCap component8();
+    method public androidx.ui.graphics.StrokeJoin component9();
+    method public androidx.ui.graphics.vector.VectorPath copy(String name, java.util.List<androidx.ui.graphics.vector.PathNode> pathData, androidx.ui.graphics.Brush? fill, float fillAlpha, androidx.ui.graphics.Brush? stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.graphics.StrokeCap strokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin, float strokeLineMiter);
     method public androidx.ui.graphics.Brush? getFill();
     method public float getFillAlpha();
     method public String getName();
diff --git a/ui/ui-framework/api/current.txt b/ui/ui-framework/api/current.txt
index 2086670..d14369a 100644
--- a/ui/ui-framework/api/current.txt
+++ b/ui/ui-framework/api/current.txt
@@ -394,8 +394,15 @@
 package androidx.ui.graphics.vector {
 
   public final class VectorAsset {
-    method public androidx.ui.core.Px getDefaultHeight();
-    method public androidx.ui.core.Px getDefaultWidth();
+    method public String component1();
+    method public androidx.ui.core.Dp component2();
+    method public androidx.ui.core.Dp component3();
+    method public float component4();
+    method public float component5();
+    method public androidx.ui.graphics.vector.VectorGroup component6();
+    method public androidx.ui.graphics.vector.VectorAsset copy(String name, androidx.ui.core.Dp defaultWidth, androidx.ui.core.Dp defaultHeight, float viewportWidth, float viewportHeight, androidx.ui.graphics.vector.VectorGroup root);
+    method public androidx.ui.core.Dp getDefaultHeight();
+    method public androidx.ui.core.Dp getDefaultWidth();
     method public String getName();
     method public androidx.ui.graphics.vector.VectorGroup getRoot();
     method public float getViewportHeight();
@@ -403,12 +410,12 @@
   }
 
   public final class VectorAssetBuilder {
-    ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+    ctor public VectorAssetBuilder(String name, androidx.ui.core.Dp defaultWidth, androidx.ui.core.Dp defaultHeight, float viewportWidth, float viewportHeight);
     method public androidx.ui.graphics.vector.VectorAssetBuilder addPath(java.util.List<androidx.ui.graphics.vector.PathNode> pathData, String name = "", androidx.ui.graphics.Brush? fill = null, float fillAlpha = 1.0f, androidx.ui.graphics.Brush? stroke = null, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.graphics.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
     method public androidx.ui.graphics.vector.VectorAsset build();
     method public void ensureNotConsumed();
-    method public androidx.ui.core.Px getDefaultHeight();
-    method public androidx.ui.core.Px getDefaultWidth();
+    method public androidx.ui.core.Dp getDefaultHeight();
+    method public androidx.ui.core.Dp getDefaultWidth();
     method public String getName();
     method public float getViewportHeight();
     method public float getViewportWidth();
@@ -430,6 +437,16 @@
   public final class VectorGroup extends androidx.ui.graphics.vector.VectorNode implements java.lang.Iterable<androidx.ui.graphics.vector.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
     ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, java.util.List<androidx.ui.graphics.vector.PathNode> clipPathData);
     ctor public VectorGroup();
+    method public String component1();
+    method public float component2();
+    method public float component3();
+    method public float component4();
+    method public float component5();
+    method public float component6();
+    method public float component7();
+    method public float component8();
+    method public java.util.List<androidx.ui.graphics.vector.PathNode> component9();
+    method public androidx.ui.graphics.vector.VectorGroup copy(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, java.util.List<androidx.ui.graphics.vector.PathNode> clipPathData);
     method public operator androidx.ui.graphics.vector.VectorNode get(int index);
     method public java.util.List<androidx.ui.graphics.vector.PathNode> getClipPathData();
     method public String getName();
@@ -450,6 +467,17 @@
 
   public final class VectorPath extends androidx.ui.graphics.vector.VectorNode {
     ctor public VectorPath(String name, java.util.List<androidx.ui.graphics.vector.PathNode> pathData, androidx.ui.graphics.Brush? fill, float fillAlpha, androidx.ui.graphics.Brush? stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.graphics.StrokeCap strokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin, float strokeLineMiter);
+    method public String component1();
+    method public float component10();
+    method public java.util.List<androidx.ui.graphics.vector.PathNode> component2();
+    method public androidx.ui.graphics.Brush? component3();
+    method public float component4();
+    method public androidx.ui.graphics.Brush? component5();
+    method public float component6();
+    method public float component7();
+    method public androidx.ui.graphics.StrokeCap component8();
+    method public androidx.ui.graphics.StrokeJoin component9();
+    method public androidx.ui.graphics.vector.VectorPath copy(String name, java.util.List<androidx.ui.graphics.vector.PathNode> pathData, androidx.ui.graphics.Brush? fill, float fillAlpha, androidx.ui.graphics.Brush? stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.graphics.StrokeCap strokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin, float strokeLineMiter);
     method public androidx.ui.graphics.Brush? getFill();
     method public float getFillAlpha();
     method public String getName();
diff --git a/ui/ui-framework/api/public_plus_experimental_0.1.0-dev04.txt b/ui/ui-framework/api/public_plus_experimental_0.1.0-dev04.txt
index 2086670..d14369a 100644
--- a/ui/ui-framework/api/public_plus_experimental_0.1.0-dev04.txt
+++ b/ui/ui-framework/api/public_plus_experimental_0.1.0-dev04.txt
@@ -394,8 +394,15 @@
 package androidx.ui.graphics.vector {
 
   public final class VectorAsset {
-    method public androidx.ui.core.Px getDefaultHeight();
-    method public androidx.ui.core.Px getDefaultWidth();
+    method public String component1();
+    method public androidx.ui.core.Dp component2();
+    method public androidx.ui.core.Dp component3();
+    method public float component4();
+    method public float component5();
+    method public androidx.ui.graphics.vector.VectorGroup component6();
+    method public androidx.ui.graphics.vector.VectorAsset copy(String name, androidx.ui.core.Dp defaultWidth, androidx.ui.core.Dp defaultHeight, float viewportWidth, float viewportHeight, androidx.ui.graphics.vector.VectorGroup root);
+    method public androidx.ui.core.Dp getDefaultHeight();
+    method public androidx.ui.core.Dp getDefaultWidth();
     method public String getName();
     method public androidx.ui.graphics.vector.VectorGroup getRoot();
     method public float getViewportHeight();
@@ -403,12 +410,12 @@
   }
 
   public final class VectorAssetBuilder {
-    ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+    ctor public VectorAssetBuilder(String name, androidx.ui.core.Dp defaultWidth, androidx.ui.core.Dp defaultHeight, float viewportWidth, float viewportHeight);
     method public androidx.ui.graphics.vector.VectorAssetBuilder addPath(java.util.List<androidx.ui.graphics.vector.PathNode> pathData, String name = "", androidx.ui.graphics.Brush? fill = null, float fillAlpha = 1.0f, androidx.ui.graphics.Brush? stroke = null, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.graphics.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
     method public androidx.ui.graphics.vector.VectorAsset build();
     method public void ensureNotConsumed();
-    method public androidx.ui.core.Px getDefaultHeight();
-    method public androidx.ui.core.Px getDefaultWidth();
+    method public androidx.ui.core.Dp getDefaultHeight();
+    method public androidx.ui.core.Dp getDefaultWidth();
     method public String getName();
     method public float getViewportHeight();
     method public float getViewportWidth();
@@ -430,6 +437,16 @@
   public final class VectorGroup extends androidx.ui.graphics.vector.VectorNode implements java.lang.Iterable<androidx.ui.graphics.vector.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
     ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, java.util.List<androidx.ui.graphics.vector.PathNode> clipPathData);
     ctor public VectorGroup();
+    method public String component1();
+    method public float component2();
+    method public float component3();
+    method public float component4();
+    method public float component5();
+    method public float component6();
+    method public float component7();
+    method public float component8();
+    method public java.util.List<androidx.ui.graphics.vector.PathNode> component9();
+    method public androidx.ui.graphics.vector.VectorGroup copy(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, java.util.List<androidx.ui.graphics.vector.PathNode> clipPathData);
     method public operator androidx.ui.graphics.vector.VectorNode get(int index);
     method public java.util.List<androidx.ui.graphics.vector.PathNode> getClipPathData();
     method public String getName();
@@ -450,6 +467,17 @@
 
   public final class VectorPath extends androidx.ui.graphics.vector.VectorNode {
     ctor public VectorPath(String name, java.util.List<androidx.ui.graphics.vector.PathNode> pathData, androidx.ui.graphics.Brush? fill, float fillAlpha, androidx.ui.graphics.Brush? stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.graphics.StrokeCap strokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin, float strokeLineMiter);
+    method public String component1();
+    method public float component10();
+    method public java.util.List<androidx.ui.graphics.vector.PathNode> component2();
+    method public androidx.ui.graphics.Brush? component3();
+    method public float component4();
+    method public androidx.ui.graphics.Brush? component5();
+    method public float component6();
+    method public float component7();
+    method public androidx.ui.graphics.StrokeCap component8();
+    method public androidx.ui.graphics.StrokeJoin component9();
+    method public androidx.ui.graphics.vector.VectorPath copy(String name, java.util.List<androidx.ui.graphics.vector.PathNode> pathData, androidx.ui.graphics.Brush? fill, float fillAlpha, androidx.ui.graphics.Brush? stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.graphics.StrokeCap strokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin, float strokeLineMiter);
     method public androidx.ui.graphics.Brush? getFill();
     method public float getFillAlpha();
     method public String getName();
diff --git a/ui/ui-framework/api/public_plus_experimental_current.txt b/ui/ui-framework/api/public_plus_experimental_current.txt
index 2086670..d14369a 100644
--- a/ui/ui-framework/api/public_plus_experimental_current.txt
+++ b/ui/ui-framework/api/public_plus_experimental_current.txt
@@ -394,8 +394,15 @@
 package androidx.ui.graphics.vector {
 
   public final class VectorAsset {
-    method public androidx.ui.core.Px getDefaultHeight();
-    method public androidx.ui.core.Px getDefaultWidth();
+    method public String component1();
+    method public androidx.ui.core.Dp component2();
+    method public androidx.ui.core.Dp component3();
+    method public float component4();
+    method public float component5();
+    method public androidx.ui.graphics.vector.VectorGroup component6();
+    method public androidx.ui.graphics.vector.VectorAsset copy(String name, androidx.ui.core.Dp defaultWidth, androidx.ui.core.Dp defaultHeight, float viewportWidth, float viewportHeight, androidx.ui.graphics.vector.VectorGroup root);
+    method public androidx.ui.core.Dp getDefaultHeight();
+    method public androidx.ui.core.Dp getDefaultWidth();
     method public String getName();
     method public androidx.ui.graphics.vector.VectorGroup getRoot();
     method public float getViewportHeight();
@@ -403,12 +410,12 @@
   }
 
   public final class VectorAssetBuilder {
-    ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+    ctor public VectorAssetBuilder(String name, androidx.ui.core.Dp defaultWidth, androidx.ui.core.Dp defaultHeight, float viewportWidth, float viewportHeight);
     method public androidx.ui.graphics.vector.VectorAssetBuilder addPath(java.util.List<androidx.ui.graphics.vector.PathNode> pathData, String name = "", androidx.ui.graphics.Brush? fill = null, float fillAlpha = 1.0f, androidx.ui.graphics.Brush? stroke = null, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.graphics.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
     method public androidx.ui.graphics.vector.VectorAsset build();
     method public void ensureNotConsumed();
-    method public androidx.ui.core.Px getDefaultHeight();
-    method public androidx.ui.core.Px getDefaultWidth();
+    method public androidx.ui.core.Dp getDefaultHeight();
+    method public androidx.ui.core.Dp getDefaultWidth();
     method public String getName();
     method public float getViewportHeight();
     method public float getViewportWidth();
@@ -430,6 +437,16 @@
   public final class VectorGroup extends androidx.ui.graphics.vector.VectorNode implements java.lang.Iterable<androidx.ui.graphics.vector.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
     ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, java.util.List<androidx.ui.graphics.vector.PathNode> clipPathData);
     ctor public VectorGroup();
+    method public String component1();
+    method public float component2();
+    method public float component3();
+    method public float component4();
+    method public float component5();
+    method public float component6();
+    method public float component7();
+    method public float component8();
+    method public java.util.List<androidx.ui.graphics.vector.PathNode> component9();
+    method public androidx.ui.graphics.vector.VectorGroup copy(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, java.util.List<androidx.ui.graphics.vector.PathNode> clipPathData);
     method public operator androidx.ui.graphics.vector.VectorNode get(int index);
     method public java.util.List<androidx.ui.graphics.vector.PathNode> getClipPathData();
     method public String getName();
@@ -450,6 +467,17 @@
 
   public final class VectorPath extends androidx.ui.graphics.vector.VectorNode {
     ctor public VectorPath(String name, java.util.List<androidx.ui.graphics.vector.PathNode> pathData, androidx.ui.graphics.Brush? fill, float fillAlpha, androidx.ui.graphics.Brush? stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.graphics.StrokeCap strokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin, float strokeLineMiter);
+    method public String component1();
+    method public float component10();
+    method public java.util.List<androidx.ui.graphics.vector.PathNode> component2();
+    method public androidx.ui.graphics.Brush? component3();
+    method public float component4();
+    method public androidx.ui.graphics.Brush? component5();
+    method public float component6();
+    method public float component7();
+    method public androidx.ui.graphics.StrokeCap component8();
+    method public androidx.ui.graphics.StrokeJoin component9();
+    method public androidx.ui.graphics.vector.VectorPath copy(String name, java.util.List<androidx.ui.graphics.vector.PathNode> pathData, androidx.ui.graphics.Brush? fill, float fillAlpha, androidx.ui.graphics.Brush? stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.graphics.StrokeCap strokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin, float strokeLineMiter);
     method public androidx.ui.graphics.Brush? getFill();
     method public float getFillAlpha();
     method public String getName();
diff --git a/ui/ui-framework/api/restricted_0.1.0-dev04.txt b/ui/ui-framework/api/restricted_0.1.0-dev04.txt
index 2086670..d14369a 100644
--- a/ui/ui-framework/api/restricted_0.1.0-dev04.txt
+++ b/ui/ui-framework/api/restricted_0.1.0-dev04.txt
@@ -394,8 +394,15 @@
 package androidx.ui.graphics.vector {
 
   public final class VectorAsset {
-    method public androidx.ui.core.Px getDefaultHeight();
-    method public androidx.ui.core.Px getDefaultWidth();
+    method public String component1();
+    method public androidx.ui.core.Dp component2();
+    method public androidx.ui.core.Dp component3();
+    method public float component4();
+    method public float component5();
+    method public androidx.ui.graphics.vector.VectorGroup component6();
+    method public androidx.ui.graphics.vector.VectorAsset copy(String name, androidx.ui.core.Dp defaultWidth, androidx.ui.core.Dp defaultHeight, float viewportWidth, float viewportHeight, androidx.ui.graphics.vector.VectorGroup root);
+    method public androidx.ui.core.Dp getDefaultHeight();
+    method public androidx.ui.core.Dp getDefaultWidth();
     method public String getName();
     method public androidx.ui.graphics.vector.VectorGroup getRoot();
     method public float getViewportHeight();
@@ -403,12 +410,12 @@
   }
 
   public final class VectorAssetBuilder {
-    ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+    ctor public VectorAssetBuilder(String name, androidx.ui.core.Dp defaultWidth, androidx.ui.core.Dp defaultHeight, float viewportWidth, float viewportHeight);
     method public androidx.ui.graphics.vector.VectorAssetBuilder addPath(java.util.List<androidx.ui.graphics.vector.PathNode> pathData, String name = "", androidx.ui.graphics.Brush? fill = null, float fillAlpha = 1.0f, androidx.ui.graphics.Brush? stroke = null, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.graphics.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
     method public androidx.ui.graphics.vector.VectorAsset build();
     method public void ensureNotConsumed();
-    method public androidx.ui.core.Px getDefaultHeight();
-    method public androidx.ui.core.Px getDefaultWidth();
+    method public androidx.ui.core.Dp getDefaultHeight();
+    method public androidx.ui.core.Dp getDefaultWidth();
     method public String getName();
     method public float getViewportHeight();
     method public float getViewportWidth();
@@ -430,6 +437,16 @@
   public final class VectorGroup extends androidx.ui.graphics.vector.VectorNode implements java.lang.Iterable<androidx.ui.graphics.vector.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
     ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, java.util.List<androidx.ui.graphics.vector.PathNode> clipPathData);
     ctor public VectorGroup();
+    method public String component1();
+    method public float component2();
+    method public float component3();
+    method public float component4();
+    method public float component5();
+    method public float component6();
+    method public float component7();
+    method public float component8();
+    method public java.util.List<androidx.ui.graphics.vector.PathNode> component9();
+    method public androidx.ui.graphics.vector.VectorGroup copy(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, java.util.List<androidx.ui.graphics.vector.PathNode> clipPathData);
     method public operator androidx.ui.graphics.vector.VectorNode get(int index);
     method public java.util.List<androidx.ui.graphics.vector.PathNode> getClipPathData();
     method public String getName();
@@ -450,6 +467,17 @@
 
   public final class VectorPath extends androidx.ui.graphics.vector.VectorNode {
     ctor public VectorPath(String name, java.util.List<androidx.ui.graphics.vector.PathNode> pathData, androidx.ui.graphics.Brush? fill, float fillAlpha, androidx.ui.graphics.Brush? stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.graphics.StrokeCap strokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin, float strokeLineMiter);
+    method public String component1();
+    method public float component10();
+    method public java.util.List<androidx.ui.graphics.vector.PathNode> component2();
+    method public androidx.ui.graphics.Brush? component3();
+    method public float component4();
+    method public androidx.ui.graphics.Brush? component5();
+    method public float component6();
+    method public float component7();
+    method public androidx.ui.graphics.StrokeCap component8();
+    method public androidx.ui.graphics.StrokeJoin component9();
+    method public androidx.ui.graphics.vector.VectorPath copy(String name, java.util.List<androidx.ui.graphics.vector.PathNode> pathData, androidx.ui.graphics.Brush? fill, float fillAlpha, androidx.ui.graphics.Brush? stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.graphics.StrokeCap strokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin, float strokeLineMiter);
     method public androidx.ui.graphics.Brush? getFill();
     method public float getFillAlpha();
     method public String getName();
diff --git a/ui/ui-framework/api/restricted_current.txt b/ui/ui-framework/api/restricted_current.txt
index 2086670..d14369a 100644
--- a/ui/ui-framework/api/restricted_current.txt
+++ b/ui/ui-framework/api/restricted_current.txt
@@ -394,8 +394,15 @@
 package androidx.ui.graphics.vector {
 
   public final class VectorAsset {
-    method public androidx.ui.core.Px getDefaultHeight();
-    method public androidx.ui.core.Px getDefaultWidth();
+    method public String component1();
+    method public androidx.ui.core.Dp component2();
+    method public androidx.ui.core.Dp component3();
+    method public float component4();
+    method public float component5();
+    method public androidx.ui.graphics.vector.VectorGroup component6();
+    method public androidx.ui.graphics.vector.VectorAsset copy(String name, androidx.ui.core.Dp defaultWidth, androidx.ui.core.Dp defaultHeight, float viewportWidth, float viewportHeight, androidx.ui.graphics.vector.VectorGroup root);
+    method public androidx.ui.core.Dp getDefaultHeight();
+    method public androidx.ui.core.Dp getDefaultWidth();
     method public String getName();
     method public androidx.ui.graphics.vector.VectorGroup getRoot();
     method public float getViewportHeight();
@@ -403,12 +410,12 @@
   }
 
   public final class VectorAssetBuilder {
-    ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+    ctor public VectorAssetBuilder(String name, androidx.ui.core.Dp defaultWidth, androidx.ui.core.Dp defaultHeight, float viewportWidth, float viewportHeight);
     method public androidx.ui.graphics.vector.VectorAssetBuilder addPath(java.util.List<androidx.ui.graphics.vector.PathNode> pathData, String name = "", androidx.ui.graphics.Brush? fill = null, float fillAlpha = 1.0f, androidx.ui.graphics.Brush? stroke = null, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.graphics.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
     method public androidx.ui.graphics.vector.VectorAsset build();
     method public void ensureNotConsumed();
-    method public androidx.ui.core.Px getDefaultHeight();
-    method public androidx.ui.core.Px getDefaultWidth();
+    method public androidx.ui.core.Dp getDefaultHeight();
+    method public androidx.ui.core.Dp getDefaultWidth();
     method public String getName();
     method public float getViewportHeight();
     method public float getViewportWidth();
@@ -430,6 +437,16 @@
   public final class VectorGroup extends androidx.ui.graphics.vector.VectorNode implements java.lang.Iterable<androidx.ui.graphics.vector.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
     ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, java.util.List<androidx.ui.graphics.vector.PathNode> clipPathData);
     ctor public VectorGroup();
+    method public String component1();
+    method public float component2();
+    method public float component3();
+    method public float component4();
+    method public float component5();
+    method public float component6();
+    method public float component7();
+    method public float component8();
+    method public java.util.List<androidx.ui.graphics.vector.PathNode> component9();
+    method public androidx.ui.graphics.vector.VectorGroup copy(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, java.util.List<androidx.ui.graphics.vector.PathNode> clipPathData);
     method public operator androidx.ui.graphics.vector.VectorNode get(int index);
     method public java.util.List<androidx.ui.graphics.vector.PathNode> getClipPathData();
     method public String getName();
@@ -450,6 +467,17 @@
 
   public final class VectorPath extends androidx.ui.graphics.vector.VectorNode {
     ctor public VectorPath(String name, java.util.List<androidx.ui.graphics.vector.PathNode> pathData, androidx.ui.graphics.Brush? fill, float fillAlpha, androidx.ui.graphics.Brush? stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.graphics.StrokeCap strokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin, float strokeLineMiter);
+    method public String component1();
+    method public float component10();
+    method public java.util.List<androidx.ui.graphics.vector.PathNode> component2();
+    method public androidx.ui.graphics.Brush? component3();
+    method public float component4();
+    method public androidx.ui.graphics.Brush? component5();
+    method public float component6();
+    method public float component7();
+    method public androidx.ui.graphics.StrokeCap component8();
+    method public androidx.ui.graphics.StrokeJoin component9();
+    method public androidx.ui.graphics.vector.VectorPath copy(String name, java.util.List<androidx.ui.graphics.vector.PathNode> pathData, androidx.ui.graphics.Brush? fill, float fillAlpha, androidx.ui.graphics.Brush? stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.graphics.StrokeCap strokeLineCap, androidx.ui.graphics.StrokeJoin strokeLineJoin, float strokeLineMiter);
     method public androidx.ui.graphics.Brush? getFill();
     method public float getFillAlpha();
     method public String getName();
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/selection/SelectionContainerTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/selection/SelectionContainerTest.kt
index bff3427..95f1530 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/selection/SelectionContainerTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/selection/SelectionContainerTest.kt
@@ -17,6 +17,7 @@
 package androidx.ui.core.selection
 
 import android.R
+import android.app.Activity
 import android.view.MotionEvent
 import android.view.View
 import android.view.ViewGroup
@@ -31,7 +32,6 @@
 import androidx.ui.core.test.ValueModel
 import androidx.ui.core.withDensity
 import androidx.ui.test.android.AndroidComposeTestRule
-import androidx.ui.test.createComposeRule
 import androidx.ui.text.TextStyle
 import androidx.ui.text.font.Font
 import androidx.ui.text.font.FontStyle
@@ -50,10 +50,10 @@
 @RunWith(JUnit4::class)
 class SelectionContainerTest {
     @get:Rule
-    val composeTestRule = createComposeRule()
+    val composeTestRule = AndroidComposeTestRule<Activity>()
 
     private val activity
-        get() = (composeTestRule as AndroidComposeTestRule).activityTestRule.activity
+        get() = composeTestRule.activityTestRule.activity
 
     private lateinit var view: View
 
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
index 9e09f6f..a11786f 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
@@ -39,6 +39,7 @@
 import androidx.ui.core.DensityAmbient
 import androidx.ui.core.DensityScope
 import androidx.ui.core.Draw
+import androidx.ui.core.DrawModifier
 import androidx.ui.core.HorizontalAlignmentLine
 import androidx.ui.core.IntPx
 import androidx.ui.core.IntPxPosition
@@ -51,6 +52,7 @@
 import androidx.ui.core.OnPositioned
 import androidx.ui.core.ParentData
 import androidx.ui.core.ParentDataModifier
+import androidx.ui.core.PxSize
 import androidx.ui.core.Ref
 import androidx.ui.core.RepaintBoundary
 import androidx.ui.core.VerticalAlignmentLine
@@ -71,6 +73,7 @@
 import androidx.ui.core.toRect
 import androidx.ui.engine.geometry.Rect
 import androidx.ui.framework.test.TestActivity
+import androidx.ui.graphics.Canvas
 import androidx.ui.graphics.Color
 import androidx.ui.graphics.Paint
 import androidx.ui.graphics.PaintingStyle
@@ -1974,6 +1977,23 @@
         )
     }
 
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun modifier_combinedModifiers() {
+        activityTestRule.runOnUiThreadIR {
+            activity.setContentInFrameLayout {
+                FixedSize(30.ipx, background(Color.Blue)) {
+                    Draw { _, _ ->
+                        drawLatch.countDown()
+                    }
+                    JustConstraints(CombinedModifier(Color.White)) {
+                    }
+                }
+            }
+        }
+        validateSquareColors(outerColor = Color.Blue, innerColor = Color.White, size = 10)
+    }
+
     private val AlignTopLeft = object : LayoutModifier {
         override fun DensityScope.modifyConstraints(constraints: Constraints) =
             constraints.looseMin()
@@ -2435,6 +2455,13 @@
     }
 }
 
+@Composable
+fun JustConstraints(modifier: Modifier, children: @Composable() () -> Unit) {
+    Layout(children, modifier) { _, constraints ->
+        layout(constraints.minWidth, constraints.minHeight) {}
+    }
+}
+
 class DrawCounterListener(private val view: View) :
     ViewTreeObserver.OnPreDrawListener {
     val latch = CountDownLatch(5)
@@ -2596,3 +2623,35 @@
     }
     canvas.drawRect(size.toRect(), paint)
 }
+
+class CombinedModifier(color: Color) : LayoutModifier, DrawModifier {
+    val paint = Paint().also { paint ->
+        paint.color = color
+        paint.style = PaintingStyle.fill
+    }
+
+    override fun DensityScope.modifyPosition(
+        childSize: IntPxSize,
+        containerSize: IntPxSize
+    ): IntPxPosition {
+        return IntPxPosition(
+            (containerSize.width - childSize.width) / 2,
+            (containerSize.height - childSize.height) / 2
+        )
+    }
+
+    override fun DensityScope.modifyConstraints(constraints: Constraints): Constraints {
+        return Constraints.tightConstraints(10.ipx, 10.ipx)
+    }
+
+    override fun DensityScope.modifySize(
+        constraints: Constraints,
+        childSize: IntPxSize
+    ): IntPxSize {
+        return IntPxSize(constraints.maxWidth, constraints.maxHeight)
+    }
+
+    override fun draw(density: Density, drawContent: () -> Unit, canvas: Canvas, size: PxSize) {
+        canvas.drawRect(size.toRect(), paint)
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/graphics/vector/VectorInvalidationTestCase.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/graphics/vector/VectorInvalidationTestCase.kt
index 57c8d8e..5a788e1 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/graphics/vector/VectorInvalidationTestCase.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/graphics/vector/VectorInvalidationTestCase.kt
@@ -48,7 +48,7 @@
         val vectorAsset = loadVectorResource(state.value)
         WithDensity {
             vectorAsset.resource.resource?.let {
-                val width = it.defaultWidth.toDp()
+                val width = it.defaultWidth
                 vectorSize = width.toIntPx().value
                 AtLeastSize(size = width.toIntPx()) {
                     Draw { canvas, parentSize ->
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/graphics/vector/compat/XmlVectorParserTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/graphics/vector/compat/XmlVectorParserTest.kt
index 4f4287c..132daea 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/graphics/vector/compat/XmlVectorParserTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/graphics/vector/compat/XmlVectorParserTest.kt
@@ -18,14 +18,12 @@
 
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
-import androidx.ui.core.Px
-
-import androidx.ui.graphics.vector.VectorPath
+import androidx.ui.core.dp
 import androidx.ui.framework.test.R
 import androidx.ui.graphics.Color
 import androidx.ui.graphics.SolidColor
 import androidx.ui.graphics.vector.PathCommand
-import androidx.ui.graphics.vector.PathNode
+import androidx.ui.graphics.vector.VectorPath
 import androidx.ui.res.loadVectorResource
 import org.junit.Assert.assertEquals
 import org.junit.Test
@@ -44,8 +42,7 @@
             res,
             R.drawable.test_compose_vector
         )
-        val density = res.displayMetrics.density
-        val expectedSize = Px(density*24)
+        val expectedSize = 24.dp
         assertEquals(expectedSize, asset.defaultWidth)
         assertEquals(expectedSize, asset.defaultHeight)
         assertEquals(24.0f, asset.viewportWidth)
@@ -57,20 +54,20 @@
 
         val path = node.pathData
         assertEquals(3, path.size)
-        assertEquals(PathCommand.MoveTo, path.get(0).command)
-        assertEquals(20.0f, path.get(0).args[0])
-        assertEquals(10.0f, path.get(0).args[1])
+        assertEquals(PathCommand.MoveTo, path[0].command)
+        assertEquals(20.0f, path[0].args[0])
+        assertEquals(10.0f, path[0].args[1])
 
-        assertEquals(PathCommand.RelativeLineTo, path.get(1).command)
-        assertEquals(6, path.get(1).args.size)
-        assertEquals(10.0f, path.get(1).args[0])
-        assertEquals(0.0f, path.get(1).args[1])
-        assertEquals(0.0f, path.get(1).args[2])
-        assertEquals(10.0f, path.get(1).args[3])
-        assertEquals(-10.0f, path.get(1).args[4])
-        assertEquals(0.0f, path.get(1).args[5])
+        assertEquals(PathCommand.RelativeLineTo, path[1].command)
+        assertEquals(6, path[1].args.size)
+        assertEquals(10.0f, path[1].args[0])
+        assertEquals(0.0f, path[1].args[1])
+        assertEquals(0.0f, path[1].args[2])
+        assertEquals(10.0f, path[1].args[3])
+        assertEquals(-10.0f, path[1].args[4])
+        assertEquals(0.0f, path[1].args[5])
 
-        assertEquals(PathCommand.RelativeClose, path.get(2).command)
-        assertEquals(0, path.get(2).args.size)
+        assertEquals(PathCommand.RelativeClose, path[2].command)
+        assertEquals(0, path[2].args.size)
     }
 }
\ No newline at end of file
diff --git a/ui/ui-framework/src/main/java/androidx/ui/graphics/vector/VectorAsset.kt b/ui/ui-framework/src/main/java/androidx/ui/graphics/vector/VectorAsset.kt
index 3ce6d70..b35b046 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/graphics/vector/VectorAsset.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/graphics/vector/VectorAsset.kt
@@ -18,7 +18,7 @@
 
 import androidx.compose.Composable
 import androidx.ui.core.Alignment
-import androidx.ui.core.Px
+import androidx.ui.core.Dp
 import androidx.ui.graphics.BlendMode
 import androidx.ui.graphics.Brush
 import androidx.ui.graphics.Color
@@ -44,14 +44,14 @@
     val name: String = DefaultGroupName,
 
     /**
-     * Intrinsic width of the Vector in pixels
+     * Intrinsic width of the Vector in [Dp]
      */
-    val defaultWidth: Px,
+    val defaultWidth: Dp,
 
     /**
-     * Intrinsic height of the Vector in pixels
+     * Intrinsic height of the Vector in [Dp]
      */
-    val defaultHeight: Px,
+    val defaultHeight: Dp,
 
     /**
      *  Used to define the width of the viewport space. Viewport is basically the virtual canvas
@@ -200,7 +200,7 @@
  * Vector graphics object that is generated as a result of [VectorAssetBuilder]]
  * It can be composed and rendered by passing it as an argument to [DrawVector]
  */
-class VectorAsset internal constructor(
+data class VectorAsset internal constructor(
 
     /**
      * Name of the Vector asset
@@ -208,14 +208,14 @@
     val name: String,
 
     /**
-     * Intrinsic width of the vector asset in pixels
+     * Intrinsic width of the vector asset in [Dp]
      */
-    val defaultWidth: Px,
+    val defaultWidth: Dp,
 
     /**
-     * Intrinsic height of the vector asset in pixels
+     * Intrinsic height of the vector asset in [Dp]
      */
-    val defaultHeight: Px,
+    val defaultHeight: Dp,
 
     /**
      *  Used to define the width of the viewport space. Viewport is basically the virtual canvas
@@ -240,7 +240,7 @@
  * The transformations are defined in the same coordinates as the viewport.
  * The transformations are applied in the order of scale, rotate then translate.
  */
-class VectorGroup(
+data class VectorGroup(
     /**
      * Name of the corresponding group
      */
@@ -317,7 +317,7 @@
  * Leaf node of a Vector graphics tree. This specifies a path shape and parameters
  * to color and style the the shape itself
  */
-class VectorPath(
+data class VectorPath(
     /**
      * Name of the corresponding path
      */
diff --git a/ui/ui-framework/src/main/java/androidx/ui/graphics/vector/compat/XmlVectorParser.kt b/ui/ui-framework/src/main/java/androidx/ui/graphics/vector/compat/XmlVectorParser.kt
index c7b248e..53be7f4 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/graphics/vector/compat/XmlVectorParser.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/graphics/vector/compat/XmlVectorParser.kt
@@ -20,17 +20,14 @@
 import android.util.AttributeSet
 import androidx.core.content.res.ComplexColorCompat
 import androidx.core.content.res.TypedArrayUtils
-import androidx.ui.core.Px
-import androidx.ui.graphics.vector.VectorAssetBuilder
+import androidx.ui.core.dp
 import androidx.ui.graphics.Brush
 import androidx.ui.graphics.Color
+import androidx.ui.graphics.Shader
 import androidx.ui.graphics.ShaderBrush
 import androidx.ui.graphics.SolidColor
-
-import androidx.ui.graphics.Shader
 import androidx.ui.graphics.StrokeCap
 import androidx.ui.graphics.StrokeJoin
-import androidx.ui.graphics.vector.addPathNodes
 import androidx.ui.graphics.vector.DefaultPivotX
 import androidx.ui.graphics.vector.DefaultPivotY
 import androidx.ui.graphics.vector.DefaultRotation
@@ -40,6 +37,8 @@
 import androidx.ui.graphics.vector.DefaultTranslationY
 import androidx.ui.graphics.vector.EmptyPath
 import androidx.ui.graphics.vector.PathNode
+import androidx.ui.graphics.vector.VectorAssetBuilder
+import androidx.ui.graphics.vector.addPathNodes
 import org.xmlpull.v1.XmlPullParser
 import org.xmlpull.v1.XmlPullParserException
 
@@ -156,13 +155,11 @@
 
     if (viewportWidth <= 0) {
         throw XmlPullParserException(
-            vectorAttrs.getPositionDescription() +
-                    "<VectorGraphic> tag requires viewportWidth > 0"
+            vectorAttrs.positionDescription + "<VectorGraphic> tag requires viewportWidth > 0"
         )
     } else if (viewportHeight <= 0) {
         throw XmlPullParserException(
-            vectorAttrs.getPositionDescription() +
-                    "<VectorGraphic> tag requires viewportHeight > 0"
+            vectorAttrs.positionDescription + "<VectorGraphic> tag requires viewportHeight > 0"
         )
     }
 
@@ -173,11 +170,14 @@
         AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_HEIGHT, 0.0f
     )
 
+    val defaultWidthDp = (defaultWidth / res.displayMetrics.density).dp
+    val defaultHeightDp = (defaultHeight / res.displayMetrics.density).dp
+
     vectorAttrs.recycle()
 
     return VectorAssetBuilder(
-        defaultWidth = Px(defaultWidth),
-        defaultHeight = Px(defaultHeight),
+        defaultWidth = defaultWidthDp,
+        defaultHeight = defaultHeightDp,
         viewportWidth = viewportWidth,
         viewportHeight = viewportHeight
     )
diff --git a/ui/ui-internal-lint-checks/src/main/java/androidx/ui/lint/UnnecessaryLambdaCreationDetector.kt b/ui/ui-internal-lint-checks/src/main/java/androidx/ui/lint/UnnecessaryLambdaCreationDetector.kt
index dc261ca..3481f8d 100644
--- a/ui/ui-internal-lint-checks/src/main/java/androidx/ui/lint/UnnecessaryLambdaCreationDetector.kt
+++ b/ui/ui-internal-lint-checks/src/main/java/androidx/ui/lint/UnnecessaryLambdaCreationDetector.kt
@@ -87,9 +87,18 @@
                 else -> null
             } ?: return
 
+            // This is the parent function call that contains the lambda expression.
+            // I.e in Foo { bar() } this will be the call to `Foo`.
             // We want to make sure this lambda is being invoked in the context of a function call,
-            // and not as a property assignment.
-            val parentExpression = node.uastParent!!.sourcePsi as? KtCallExpression ?: return
+            // and not as a property assignment - so we cast to KotlinUFunctionCallExpression to
+            // filter out such cases.
+            val parentExpression = (node.uastParent as? KotlinUFunctionCallExpression) ?: return
+
+            // If we can't resolve the parent call, then the parent function is defined in a
+            // separate module, so we don't have the right metadata - and hence the argumentType
+            // below will be Function0 even if in the actual source it has a scope. Return early to
+            // avoid false positives.
+            parentExpression.resolve() ?: return
 
             // If the expression has no receiver, it is not a lambda invocation
             val functionType = expression.receiverType as? PsiClassReferenceType ?: return
@@ -111,7 +120,11 @@
             // shouldn't matter that much in practice.
             if (functionType.reference.canonicalText.contains(NonExistentClass)) return
 
-            if (parentExpression.isComponentNodeInvocation()) return
+            // Component nodes are classes that are invoked as if they are a function call, but
+            // they aren't actually a function call and so they cannot be inlined. Unfortunately
+            // since this is done in a compiler plugin, when running lint we don't have a way to
+            // understand this better, so we just check to see if the name looks like it is a node.
+            if (parentExpression.isComponentNodeInvocation) return
 
             val lambdaName = expression.methodIdentifier!!.name
             if (node.valueParameters.any { it.name == lambdaName }) return
@@ -126,8 +139,9 @@
     }
 
     companion object {
-        private fun KtCallExpression.isComponentNodeInvocation() =
-            referenceExpression()!!.text.endsWith("Node")
+        private val KotlinUFunctionCallExpression.isComponentNodeInvocation
+            get() = (sourcePsi as? KtCallExpression)?.referenceExpression()?.text
+                ?.endsWith("Node") == true
 
         private const val NonExistentClass = "error.NonExistentClass"
 
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/ConstraintLayout.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/ConstraintLayout.kt
index 5596d0b..29cdffa 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/ConstraintLayout.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/ConstraintLayout.kt
@@ -38,6 +38,7 @@
 import androidx.ui.core.Layout
 import androidx.ui.core.LayoutModifier
 import androidx.ui.core.Measurable
+import androidx.ui.core.ParentDataModifier
 import androidx.ui.core.Placeable
 import androidx.ui.core.Placeable.PlacementScope.place
 import androidx.ui.core.dp
@@ -656,7 +657,7 @@
         get() = verticalBehavior == ConstraintWidget.DimensionBehaviour.WRAP_CONTENT
 }
 
-private data class TagModifier(val tag: Any) : LayoutModifier {
+private data class TagModifier(val tag: Any) : LayoutModifier, ParentDataModifier {
     override fun DensityScope.modifyConstraints(constraints: Constraints) = constraints
     override fun DensityScope.modifySize(constraints: Constraints, childSize: IntPxSize) = childSize
     override fun DensityScope.minIntrinsicWidthOf(measurable: Measurable, height: IntPx) =
diff --git a/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt b/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
index e5bf326..24f380c 100644
--- a/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
+++ b/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
@@ -1015,12 +1015,17 @@
             // Rebuild layoutNodeWrapper
             val oldPlaceable = layoutNodeWrapper
             layoutNodeWrapper = modifier.foldOut(innerLayoutNodeWrapper) { mod, toWrap ->
-                when (mod) {
-                    is LayoutModifier -> ModifiedLayoutNode(toWrap, mod)
-                    is ParentDataModifier -> ModifiedParentDataNode(toWrap, mod)
-                    is DrawModifier -> ModifiedDrawNode(toWrap, mod)
-                    else -> toWrap
+                var wrapper = toWrap
+                if (mod is DrawModifier) {
+                    wrapper = ModifiedDrawNode(wrapper, mod)
                 }
+                if (mod is LayoutModifier) {
+                    wrapper = ModifiedLayoutNode(wrapper, mod)
+                }
+                if (mod is ParentDataModifier) {
+                    wrapper = ModifiedParentDataNode(wrapper, mod)
+                }
+                wrapper
             }
             // Optimize the case where the layout itself is not modified. A common reason for
             // this is if no wrapping actually occurs above because no LayoutModifiers are
@@ -1182,7 +1187,7 @@
     private inner class ModifiedLayoutNode(
         override val wrapped: LayoutNodeWrapper,
         val layoutModifier: LayoutModifier
-    ) : ModifiedParentDataNode(wrapped, layoutModifier) {
+    ) : DelegatingLayoutNodeWrapper() {
 
         /**
          * The [Placeable] returned by measuring [wrapped] in [measure].
diff --git a/ui/ui-test/api/0.1.0-dev04.txt b/ui/ui-test/api/0.1.0-dev04.txt
index 133d226..fe1c8b5 100644
--- a/ui/ui-test/api/0.1.0-dev04.txt
+++ b/ui/ui-test/api/0.1.0-dev04.txt
@@ -103,8 +103,8 @@
     method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
-    method public void runOnIdleCompose(kotlin.jvm.functions.Function0<kotlin.Unit> action);
-    method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public <T> T! runOnIdleCompose(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);
     property public abstract androidx.ui.core.Density density;
     property public abstract android.util.DisplayMetrics displayMetrics;
@@ -191,20 +191,19 @@
     method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
   }
 
-  public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
-    ctor public AndroidComposeTestRule(boolean disableTransitions);
-    ctor public AndroidComposeTestRule();
+  public final class AndroidComposeTestRule<T extends android.app.Activity> implements androidx.ui.test.ComposeTestRule {
+    ctor public AndroidComposeTestRule(Class<T> activityClass, boolean disableTransitions);
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
     method @RequiresApi(android.os.Build.VERSION_CODES.O) public android.graphics.Bitmap captureScreenOnIdle();
     method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
     method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
-    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
+    method public androidx.test.rule.ActivityTestRule<T> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
-    method public void runOnIdleCompose(kotlin.jvm.functions.Function0<kotlin.Unit> action);
-    method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public <T> T! runOnIdleCompose(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);
-    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<T> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -214,6 +213,10 @@
     method public void evaluate();
   }
 
+  public final class AndroidComposeTestRuleKt {
+    method public static inline <reified T extends android.app.Activity> androidx.ui.test.android.AndroidComposeTestRule<T> AndroidComposeTestRule(boolean disableTransitions = false);
+  }
+
   public final class ComposeIdlingResourceKt {
     method public static void registerComposeWithEspresso();
     method public static void unregisterComposeFromEspresso();
diff --git a/ui/ui-test/api/current.txt b/ui/ui-test/api/current.txt
index 133d226..fe1c8b5 100644
--- a/ui/ui-test/api/current.txt
+++ b/ui/ui-test/api/current.txt
@@ -103,8 +103,8 @@
     method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
-    method public void runOnIdleCompose(kotlin.jvm.functions.Function0<kotlin.Unit> action);
-    method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public <T> T! runOnIdleCompose(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);
     property public abstract androidx.ui.core.Density density;
     property public abstract android.util.DisplayMetrics displayMetrics;
@@ -191,20 +191,19 @@
     method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
   }
 
-  public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
-    ctor public AndroidComposeTestRule(boolean disableTransitions);
-    ctor public AndroidComposeTestRule();
+  public final class AndroidComposeTestRule<T extends android.app.Activity> implements androidx.ui.test.ComposeTestRule {
+    ctor public AndroidComposeTestRule(Class<T> activityClass, boolean disableTransitions);
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
     method @RequiresApi(android.os.Build.VERSION_CODES.O) public android.graphics.Bitmap captureScreenOnIdle();
     method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
     method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
-    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
+    method public androidx.test.rule.ActivityTestRule<T> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
-    method public void runOnIdleCompose(kotlin.jvm.functions.Function0<kotlin.Unit> action);
-    method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public <T> T! runOnIdleCompose(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);
-    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<T> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -214,6 +213,10 @@
     method public void evaluate();
   }
 
+  public final class AndroidComposeTestRuleKt {
+    method public static inline <reified T extends android.app.Activity> androidx.ui.test.android.AndroidComposeTestRule<T> AndroidComposeTestRule(boolean disableTransitions = false);
+  }
+
   public final class ComposeIdlingResourceKt {
     method public static void registerComposeWithEspresso();
     method public static void unregisterComposeFromEspresso();
diff --git a/ui/ui-test/api/public_plus_experimental_0.1.0-dev04.txt b/ui/ui-test/api/public_plus_experimental_0.1.0-dev04.txt
index 133d226..fe1c8b5 100644
--- a/ui/ui-test/api/public_plus_experimental_0.1.0-dev04.txt
+++ b/ui/ui-test/api/public_plus_experimental_0.1.0-dev04.txt
@@ -103,8 +103,8 @@
     method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
-    method public void runOnIdleCompose(kotlin.jvm.functions.Function0<kotlin.Unit> action);
-    method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public <T> T! runOnIdleCompose(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);
     property public abstract androidx.ui.core.Density density;
     property public abstract android.util.DisplayMetrics displayMetrics;
@@ -191,20 +191,19 @@
     method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
   }
 
-  public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
-    ctor public AndroidComposeTestRule(boolean disableTransitions);
-    ctor public AndroidComposeTestRule();
+  public final class AndroidComposeTestRule<T extends android.app.Activity> implements androidx.ui.test.ComposeTestRule {
+    ctor public AndroidComposeTestRule(Class<T> activityClass, boolean disableTransitions);
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
     method @RequiresApi(android.os.Build.VERSION_CODES.O) public android.graphics.Bitmap captureScreenOnIdle();
     method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
     method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
-    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
+    method public androidx.test.rule.ActivityTestRule<T> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
-    method public void runOnIdleCompose(kotlin.jvm.functions.Function0<kotlin.Unit> action);
-    method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public <T> T! runOnIdleCompose(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);
-    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<T> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -214,6 +213,10 @@
     method public void evaluate();
   }
 
+  public final class AndroidComposeTestRuleKt {
+    method public static inline <reified T extends android.app.Activity> androidx.ui.test.android.AndroidComposeTestRule<T> AndroidComposeTestRule(boolean disableTransitions = false);
+  }
+
   public final class ComposeIdlingResourceKt {
     method public static void registerComposeWithEspresso();
     method public static void unregisterComposeFromEspresso();
diff --git a/ui/ui-test/api/public_plus_experimental_current.txt b/ui/ui-test/api/public_plus_experimental_current.txt
index 133d226..fe1c8b5 100644
--- a/ui/ui-test/api/public_plus_experimental_current.txt
+++ b/ui/ui-test/api/public_plus_experimental_current.txt
@@ -103,8 +103,8 @@
     method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
-    method public void runOnIdleCompose(kotlin.jvm.functions.Function0<kotlin.Unit> action);
-    method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public <T> T! runOnIdleCompose(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);
     property public abstract androidx.ui.core.Density density;
     property public abstract android.util.DisplayMetrics displayMetrics;
@@ -191,20 +191,19 @@
     method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
   }
 
-  public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
-    ctor public AndroidComposeTestRule(boolean disableTransitions);
-    ctor public AndroidComposeTestRule();
+  public final class AndroidComposeTestRule<T extends android.app.Activity> implements androidx.ui.test.ComposeTestRule {
+    ctor public AndroidComposeTestRule(Class<T> activityClass, boolean disableTransitions);
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
     method @RequiresApi(android.os.Build.VERSION_CODES.O) public android.graphics.Bitmap captureScreenOnIdle();
     method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
     method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
-    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
+    method public androidx.test.rule.ActivityTestRule<T> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
-    method public void runOnIdleCompose(kotlin.jvm.functions.Function0<kotlin.Unit> action);
-    method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public <T> T! runOnIdleCompose(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);
-    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<T> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -214,6 +213,10 @@
     method public void evaluate();
   }
 
+  public final class AndroidComposeTestRuleKt {
+    method public static inline <reified T extends android.app.Activity> androidx.ui.test.android.AndroidComposeTestRule<T> AndroidComposeTestRule(boolean disableTransitions = false);
+  }
+
   public final class ComposeIdlingResourceKt {
     method public static void registerComposeWithEspresso();
     method public static void unregisterComposeFromEspresso();
diff --git a/ui/ui-test/api/restricted_0.1.0-dev04.txt b/ui/ui-test/api/restricted_0.1.0-dev04.txt
index 133d226..fe1c8b5 100644
--- a/ui/ui-test/api/restricted_0.1.0-dev04.txt
+++ b/ui/ui-test/api/restricted_0.1.0-dev04.txt
@@ -103,8 +103,8 @@
     method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
-    method public void runOnIdleCompose(kotlin.jvm.functions.Function0<kotlin.Unit> action);
-    method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public <T> T! runOnIdleCompose(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);
     property public abstract androidx.ui.core.Density density;
     property public abstract android.util.DisplayMetrics displayMetrics;
@@ -191,20 +191,19 @@
     method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
   }
 
-  public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
-    ctor public AndroidComposeTestRule(boolean disableTransitions);
-    ctor public AndroidComposeTestRule();
+  public final class AndroidComposeTestRule<T extends android.app.Activity> implements androidx.ui.test.ComposeTestRule {
+    ctor public AndroidComposeTestRule(Class<T> activityClass, boolean disableTransitions);
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
     method @RequiresApi(android.os.Build.VERSION_CODES.O) public android.graphics.Bitmap captureScreenOnIdle();
     method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
     method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
-    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
+    method public androidx.test.rule.ActivityTestRule<T> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
-    method public void runOnIdleCompose(kotlin.jvm.functions.Function0<kotlin.Unit> action);
-    method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public <T> T! runOnIdleCompose(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);
-    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<T> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -214,6 +213,10 @@
     method public void evaluate();
   }
 
+  public final class AndroidComposeTestRuleKt {
+    method public static inline <reified T extends android.app.Activity> androidx.ui.test.android.AndroidComposeTestRule<T> AndroidComposeTestRule(boolean disableTransitions = false);
+  }
+
   public final class ComposeIdlingResourceKt {
     method public static void registerComposeWithEspresso();
     method public static void unregisterComposeFromEspresso();
diff --git a/ui/ui-test/api/restricted_current.txt b/ui/ui-test/api/restricted_current.txt
index 133d226..fe1c8b5 100644
--- a/ui/ui-test/api/restricted_current.txt
+++ b/ui/ui-test/api/restricted_current.txt
@@ -103,8 +103,8 @@
     method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
-    method public void runOnIdleCompose(kotlin.jvm.functions.Function0<kotlin.Unit> action);
-    method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public <T> T! runOnIdleCompose(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);
     property public abstract androidx.ui.core.Density density;
     property public abstract android.util.DisplayMetrics displayMetrics;
@@ -191,20 +191,19 @@
     method public void performTestWithEventsControl(kotlin.jvm.functions.Function1<? super androidx.ui.test.ComposeExecutionControl,kotlin.Unit> block);
   }
 
-  public final class AndroidComposeTestRule implements androidx.ui.test.ComposeTestRule {
-    ctor public AndroidComposeTestRule(boolean disableTransitions);
-    ctor public AndroidComposeTestRule();
+  public final class AndroidComposeTestRule<T extends android.app.Activity> implements androidx.ui.test.ComposeTestRule {
+    ctor public AndroidComposeTestRule(Class<T> activityClass, boolean disableTransitions);
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description? description);
     method @RequiresApi(android.os.Build.VERSION_CODES.O) public android.graphics.Bitmap captureScreenOnIdle();
     method public androidx.ui.test.ComposeTestCaseSetup forGivenContent(kotlin.jvm.functions.Function0<kotlin.Unit> composable);
     method public androidx.ui.test.ComposeTestCaseSetup forGivenTestCase(androidx.ui.test.ComposeTestCase testCase);
-    method public androidx.test.rule.ActivityTestRule<android.app.Activity> getActivityTestRule();
+    method public androidx.test.rule.ActivityTestRule<T> getActivityTestRule();
     method public androidx.ui.core.Density getDensity();
     method public android.util.DisplayMetrics getDisplayMetrics();
-    method public void runOnIdleCompose(kotlin.jvm.functions.Function0<kotlin.Unit> action);
-    method public void runOnUiThread(kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public <T> T! runOnIdleCompose(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);
-    property public final androidx.test.rule.ActivityTestRule<android.app.Activity> activityTestRule;
+    property public final androidx.test.rule.ActivityTestRule<T> activityTestRule;
     property public androidx.ui.core.Density density;
     property public android.util.DisplayMetrics displayMetrics;
   }
@@ -214,6 +213,10 @@
     method public void evaluate();
   }
 
+  public final class AndroidComposeTestRuleKt {
+    method public static inline <reified T extends android.app.Activity> androidx.ui.test.android.AndroidComposeTestRule<T> AndroidComposeTestRule(boolean disableTransitions = false);
+  }
+
   public final class ComposeIdlingResourceKt {
     method public static void registerComposeWithEspresso();
     method public static void unregisterComposeFromEspresso();
diff --git a/ui/ui-test/src/androidTest/AndroidManifest.xml b/ui/ui-test/src/androidTest/AndroidManifest.xml
index 3c799d0..b08a496a 100644
--- a/ui/ui-test/src/androidTest/AndroidManifest.xml
+++ b/ui/ui-test/src/androidTest/AndroidManifest.xml
@@ -18,6 +18,8 @@
      <application>
          <activity android:name="android.app.Activity"
              android:theme="@android:style/Theme.Material.NoActionBar.Fullscreen"/>
+         <activity android:name="androidx.ui.test.CustomActivity"
+             android:theme="@android:style/Theme.Material.NoActionBar.Fullscreen"/>
     </application>
 </manifest>
 
diff --git a/ui/ui-test/src/androidTest/java/androidx/ui/test/CustomActivityTest.kt b/ui/ui-test/src/androidTest/java/androidx/ui/test/CustomActivityTest.kt
new file mode 100644
index 0000000..0a431d0
--- /dev/null
+++ b/ui/ui-test/src/androidTest/java/androidx/ui/test/CustomActivityTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.test
+
+import android.app.Activity
+import android.os.Bundle
+import androidx.test.filters.MediumTest
+import androidx.ui.core.setContent
+import androidx.ui.layout.Stack
+import androidx.ui.material.Button
+import androidx.ui.material.MaterialTheme
+import androidx.ui.test.android.AndroidComposeTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+class CustomActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setContent {
+            MaterialTheme {
+                Stack {
+                    Button(text = "Hello")
+                }
+            }
+        }
+    }
+}
+
+/**
+ * Tests that we can launch custom activities via [AndroidComposeTestRule].
+ */
+@MediumTest
+@RunWith(JUnit4::class)
+class CustomActivityTest {
+
+    @get:Rule
+    val testRule = AndroidComposeTestRule<CustomActivity>()
+
+    @Test
+    fun launchCustomActivity() {
+        findByText("Hello").assertIsVisible()
+    }
+}
diff --git a/ui/ui-test/src/main/java/androidx/ui/test/ComposeTestRule.kt b/ui/ui-test/src/main/java/androidx/ui/test/ComposeTestRule.kt
index bb05ada..8436ffb 100644
--- a/ui/ui-test/src/main/java/androidx/ui/test/ComposeTestRule.kt
+++ b/ui/ui-test/src/main/java/androidx/ui/test/ComposeTestRule.kt
@@ -16,6 +16,7 @@
 
 package androidx.ui.test
 
+import android.app.Activity
 import android.graphics.Bitmap
 import android.os.Build
 import android.util.DisplayMetrics
@@ -66,7 +67,7 @@
      *
      * This method is blocking until the action is complete.
      */
-    fun runOnUiThread(action: () -> Unit)
+    fun <T> runOnUiThread(action: () -> T): T
 
     /**
      * Executes the given action in the same way as [runOnUiThread] but also makes sure Compose
@@ -75,7 +76,7 @@
      *
      * This method is blocking until the action is complete.
      */
-    fun runOnIdleCompose(action: () -> Unit)
+    fun <T> runOnIdleCompose(action: () -> T): T
 
     /**
      * Takes screenshot of the Activity's window after Compose UI gets idle.
@@ -112,7 +113,14 @@
 
 /**
  * Factory method to provide implementation of [ComposeTestRule].
+ *
+ * This method is useful for tests in compose libraries where no custom Activity is usually
+ * needed. For app tests or launching custom activities, see [AndroidComposeTestRule].
+ *
+ * For Android this will use the default Activity (android.app.Activity). You need to add a
+ * reference to this activity into the manifest file of the corresponding tests (usually in
+ * androidTest/AndroidManifest.xml).
  */
 fun createComposeRule(disableTransitions: Boolean = false): ComposeTestRule {
-    return AndroidComposeTestRule(disableTransitions)
+    return AndroidComposeTestRule(Activity::class.java, disableTransitions)
 }
diff --git a/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestRule.kt b/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestRule.kt
index 70ff64f..7c4044f 100644
--- a/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestRule.kt
+++ b/ui/ui-test/src/main/java/androidx/ui/test/android/AndroidComposeTestRule.kt
@@ -43,13 +43,34 @@
 import java.util.concurrent.TimeUnit
 
 /**
+ * Factory method to provide implementation of [AndroidComposeTestRule].
+ *
+ * This method is useful for tests that require a custom Activity. This is usually the case for
+ * app tests. Make sure that you add the provided activity into your app's manifest file (usually
+ * in main/AndroidManifest.xml).
+ *
+ * If you don't care about specific activity and just want to test composables in general, see
+ * [AndroidComposeTestRule].
+ */
+inline fun <reified T : Activity> AndroidComposeTestRule(
+    disableTransitions: Boolean = false
+): AndroidComposeTestRule<T> {
+    // TODO(b/138993381): By launching custom activities we are losing control over what content is
+    // already there. This is issue in case the user already set some compose content and decides
+    // to set it again via our API. In such case we won't be able to dispose the old composition.
+    // Other option would be to provide a smaller interface that does not expose these methods.
+    return AndroidComposeTestRule(T::class.java, disableTransitions)
+}
+
+/**
  * Android specific implementation of [ComposeTestRule].
  */
-class AndroidComposeTestRule(
+class AndroidComposeTestRule<T : Activity>(
+    activityClass: Class<T>,
     private val disableTransitions: Boolean = false
 ) : ComposeTestRule {
 
-    val activityTestRule = ActivityTestRule<Activity>(Activity::class.java)
+    val activityTestRule = ActivityTestRule<T>(activityClass)
 
     private val handler: Handler = Handler(Looper.getMainLooper())
     private var disposeContentHook: (() -> Unit)? = null
@@ -63,20 +84,22 @@
         return activityTestRule.apply(AndroidComposeStatement(base), description)
     }
 
-    override fun runOnUiThread(action: () -> Unit) {
+    override fun <T> runOnUiThread(action: () -> T): T {
         // Workaround for lambda bug in IR
+        var result: T? = null
         activityTestRule.runOnUiThread(object : Runnable {
             override fun run() {
-                action.invoke()
+                result = action.invoke()
             }
         })
+        return result!!
     }
 
-    override fun runOnIdleCompose(action: () -> Unit) {
+    override fun <T> runOnIdleCompose(action: () -> T): T {
         // Method below make sure that compose is idle.
         SynchronizedTreeCollector.waitForIdle()
         // Execute the action on ui thread in a blocking way.
-        runOnUiThread(action)
+        return runOnUiThread(action)
     }
 
     /**
diff --git a/viewpager2/integration-tests/testapp/build.gradle b/viewpager2/integration-tests/testapp/build.gradle
index 6cdafa6..a2268a1 100644
--- a/viewpager2/integration-tests/testapp/build.gradle
+++ b/viewpager2/integration-tests/testapp/build.gradle
@@ -35,7 +35,7 @@
 
 dependencies {
     api(KOTLIN_STDLIB)
-    implementation(project(":viewpager2"))
+    implementation(project(":viewpager2:viewpager2"))
     implementation("androidx.activity:activity-ktx:1.0.0")
     implementation("com.google.android.material:material:1.1.0-alpha08") {
         exclude group: 'androidx.viewpager2', module: 'viewpager2'
diff --git a/viewpager2/api/1.0.0-alpha02.txt b/viewpager2/viewpager2/api/1.0.0-alpha02.txt
similarity index 100%
rename from viewpager2/api/1.0.0-alpha02.txt
rename to viewpager2/viewpager2/api/1.0.0-alpha02.txt
diff --git a/viewpager2/api/1.0.0-alpha03.txt b/viewpager2/viewpager2/api/1.0.0-alpha03.txt
similarity index 100%
rename from viewpager2/api/1.0.0-alpha03.txt
rename to viewpager2/viewpager2/api/1.0.0-alpha03.txt
diff --git a/viewpager2/api/1.0.0-alpha04.txt b/viewpager2/viewpager2/api/1.0.0-alpha04.txt
similarity index 100%
rename from viewpager2/api/1.0.0-alpha04.txt
rename to viewpager2/viewpager2/api/1.0.0-alpha04.txt
diff --git a/viewpager2/api/1.0.0-alpha05.txt b/viewpager2/viewpager2/api/1.0.0-alpha05.txt
similarity index 100%
rename from viewpager2/api/1.0.0-alpha05.txt
rename to viewpager2/viewpager2/api/1.0.0-alpha05.txt
diff --git a/viewpager2/api/1.0.0-alpha06.txt b/viewpager2/viewpager2/api/1.0.0-alpha06.txt
similarity index 100%
rename from viewpager2/api/1.0.0-alpha06.txt
rename to viewpager2/viewpager2/api/1.0.0-alpha06.txt
diff --git a/viewpager2/api/1.0.0-beta01.txt b/viewpager2/viewpager2/api/1.0.0-beta01.txt
similarity index 100%
rename from viewpager2/api/1.0.0-beta01.txt
rename to viewpager2/viewpager2/api/1.0.0-beta01.txt
diff --git a/viewpager2/api/1.0.0-beta02.txt b/viewpager2/viewpager2/api/1.0.0-beta02.txt
similarity index 100%
rename from viewpager2/api/1.0.0-beta02.txt
rename to viewpager2/viewpager2/api/1.0.0-beta02.txt
diff --git a/viewpager2/api/1.0.0-beta03.txt b/viewpager2/viewpager2/api/1.0.0-beta03.txt
similarity index 100%
rename from viewpager2/api/1.0.0-beta03.txt
rename to viewpager2/viewpager2/api/1.0.0-beta03.txt
diff --git a/viewpager2/api/1.0.0-beta04.txt b/viewpager2/viewpager2/api/1.0.0-beta04.txt
similarity index 100%
rename from viewpager2/api/1.0.0-beta04.txt
rename to viewpager2/viewpager2/api/1.0.0-beta04.txt
diff --git a/viewpager2/api/1.0.0-beta05.txt b/viewpager2/viewpager2/api/1.0.0-beta05.txt
similarity index 100%
rename from viewpager2/api/1.0.0-beta05.txt
rename to viewpager2/viewpager2/api/1.0.0-beta05.txt
diff --git a/viewpager2/api/1.0.0-beta06.txt b/viewpager2/viewpager2/api/1.0.0-beta06.txt
similarity index 100%
rename from viewpager2/api/1.0.0-beta06.txt
rename to viewpager2/viewpager2/api/1.0.0-beta06.txt
diff --git a/viewpager2/api/1.0.0-rc01.txt b/viewpager2/viewpager2/api/1.0.0-rc01.txt
similarity index 100%
rename from viewpager2/api/1.0.0-rc01.txt
rename to viewpager2/viewpager2/api/1.0.0-rc01.txt
diff --git a/viewpager2/api/1.1.0-alpha01.txt b/viewpager2/viewpager2/api/1.1.0-alpha01.txt
similarity index 100%
rename from viewpager2/api/1.1.0-alpha01.txt
rename to viewpager2/viewpager2/api/1.1.0-alpha01.txt
diff --git a/viewpager2/api/api_lint.ignore b/viewpager2/viewpager2/api/api_lint.ignore
similarity index 100%
rename from viewpager2/api/api_lint.ignore
rename to viewpager2/viewpager2/api/api_lint.ignore
diff --git a/viewpager2/api/current.txt b/viewpager2/viewpager2/api/current.txt
similarity index 100%
rename from viewpager2/api/current.txt
rename to viewpager2/viewpager2/api/current.txt
diff --git a/viewpager2/api/public_plus_experimental_1.0.0-alpha01.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.0.0-alpha01.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.0.0-alpha01.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.0.0-alpha01.txt
diff --git a/viewpager2/api/public_plus_experimental_1.0.0-alpha02.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.0.0-alpha02.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.0.0-alpha02.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.0.0-alpha02.txt
diff --git a/viewpager2/api/public_plus_experimental_1.0.0-alpha03.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.0.0-alpha03.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.0.0-alpha03.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.0.0-alpha03.txt
diff --git a/viewpager2/api/public_plus_experimental_1.0.0-alpha04.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.0.0-alpha04.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.0.0-alpha04.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.0.0-alpha04.txt
diff --git a/viewpager2/api/public_plus_experimental_1.0.0-alpha05.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.0.0-alpha05.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.0.0-alpha05.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.0.0-alpha05.txt
diff --git a/viewpager2/api/public_plus_experimental_1.0.0-alpha06.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.0.0-alpha06.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.0.0-alpha06.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.0.0-alpha06.txt
diff --git a/viewpager2/api/public_plus_experimental_1.0.0-beta01.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.0.0-beta01.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.0.0-beta01.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.0.0-beta01.txt
diff --git a/viewpager2/api/public_plus_experimental_1.0.0-beta02.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.0.0-beta02.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.0.0-beta02.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.0.0-beta02.txt
diff --git a/viewpager2/api/public_plus_experimental_1.0.0-beta03.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.0.0-beta03.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.0.0-beta03.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.0.0-beta03.txt
diff --git a/viewpager2/api/public_plus_experimental_1.0.0-beta04.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.0.0-beta04.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.0.0-beta04.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.0.0-beta04.txt
diff --git a/viewpager2/api/public_plus_experimental_1.0.0-beta05.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.0.0-beta05.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.0.0-beta05.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.0.0-beta05.txt
diff --git a/viewpager2/api/public_plus_experimental_1.0.0-beta06.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.0.0-beta06.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.0.0-beta06.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.0.0-beta06.txt
diff --git a/viewpager2/api/public_plus_experimental_1.0.0-rc01.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.0.0-rc01.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.0.0-rc01.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.0.0-rc01.txt
diff --git a/viewpager2/api/public_plus_experimental_1.1.0-alpha01.txt b/viewpager2/viewpager2/api/public_plus_experimental_1.1.0-alpha01.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_1.1.0-alpha01.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_1.1.0-alpha01.txt
diff --git a/viewpager2/api/public_plus_experimental_current.txt b/viewpager2/viewpager2/api/public_plus_experimental_current.txt
similarity index 100%
rename from viewpager2/api/public_plus_experimental_current.txt
rename to viewpager2/viewpager2/api/public_plus_experimental_current.txt
diff --git a/viewpager2/api/res-1.0.0-alpha01.txt b/viewpager2/viewpager2/api/res-1.0.0-alpha01.txt
similarity index 100%
rename from viewpager2/api/res-1.0.0-alpha01.txt
rename to viewpager2/viewpager2/api/res-1.0.0-alpha01.txt
diff --git a/viewpager2/api/res-1.0.0-alpha02.txt b/viewpager2/viewpager2/api/res-1.0.0-alpha02.txt
similarity index 100%
rename from viewpager2/api/res-1.0.0-alpha02.txt
rename to viewpager2/viewpager2/api/res-1.0.0-alpha02.txt
diff --git a/viewpager2/api/res-1.0.0-alpha03.txt b/viewpager2/viewpager2/api/res-1.0.0-alpha03.txt
similarity index 100%
rename from viewpager2/api/res-1.0.0-alpha03.txt
rename to viewpager2/viewpager2/api/res-1.0.0-alpha03.txt
diff --git a/viewpager2/api/res-1.0.0-alpha04.txt b/viewpager2/viewpager2/api/res-1.0.0-alpha04.txt
similarity index 100%
rename from viewpager2/api/res-1.0.0-alpha04.txt
rename to viewpager2/viewpager2/api/res-1.0.0-alpha04.txt
diff --git a/viewpager2/api/res-1.0.0-alpha05.txt b/viewpager2/viewpager2/api/res-1.0.0-alpha05.txt
similarity index 100%
rename from viewpager2/api/res-1.0.0-alpha05.txt
rename to viewpager2/viewpager2/api/res-1.0.0-alpha05.txt
diff --git a/viewpager2/api/res-1.0.0-alpha06.txt b/viewpager2/viewpager2/api/res-1.0.0-alpha06.txt
similarity index 100%
rename from viewpager2/api/res-1.0.0-alpha06.txt
rename to viewpager2/viewpager2/api/res-1.0.0-alpha06.txt
diff --git a/viewpager2/api/res-1.0.0-beta01.txt b/viewpager2/viewpager2/api/res-1.0.0-beta01.txt
similarity index 100%
rename from viewpager2/api/res-1.0.0-beta01.txt
rename to viewpager2/viewpager2/api/res-1.0.0-beta01.txt
diff --git a/viewpager2/api/res-1.0.0-beta02.txt b/viewpager2/viewpager2/api/res-1.0.0-beta02.txt
similarity index 100%
rename from viewpager2/api/res-1.0.0-beta02.txt
rename to viewpager2/viewpager2/api/res-1.0.0-beta02.txt
diff --git a/viewpager2/api/res-1.0.0-beta03.txt b/viewpager2/viewpager2/api/res-1.0.0-beta03.txt
similarity index 100%
rename from viewpager2/api/res-1.0.0-beta03.txt
rename to viewpager2/viewpager2/api/res-1.0.0-beta03.txt
diff --git a/viewpager2/api/res-1.0.0-beta04.txt b/viewpager2/viewpager2/api/res-1.0.0-beta04.txt
similarity index 100%
rename from viewpager2/api/res-1.0.0-beta04.txt
rename to viewpager2/viewpager2/api/res-1.0.0-beta04.txt
diff --git a/viewpager2/api/res-1.0.0-beta05.txt b/viewpager2/viewpager2/api/res-1.0.0-beta05.txt
similarity index 100%
rename from viewpager2/api/res-1.0.0-beta05.txt
rename to viewpager2/viewpager2/api/res-1.0.0-beta05.txt
diff --git a/viewpager2/api/res-1.0.0-beta06.txt b/viewpager2/viewpager2/api/res-1.0.0-beta06.txt
similarity index 100%
rename from viewpager2/api/res-1.0.0-beta06.txt
rename to viewpager2/viewpager2/api/res-1.0.0-beta06.txt
diff --git a/viewpager2/api/res-1.0.0-rc01.txt b/viewpager2/viewpager2/api/res-1.0.0-rc01.txt
similarity index 100%
rename from viewpager2/api/res-1.0.0-rc01.txt
rename to viewpager2/viewpager2/api/res-1.0.0-rc01.txt
diff --git a/viewpager2/api/res-1.1.0-alpha01.txt b/viewpager2/viewpager2/api/res-1.1.0-alpha01.txt
similarity index 100%
rename from viewpager2/api/res-1.1.0-alpha01.txt
rename to viewpager2/viewpager2/api/res-1.1.0-alpha01.txt
diff --git a/viewpager2/api/restricted_1.0.0-alpha02.txt b/viewpager2/viewpager2/api/restricted_1.0.0-alpha02.txt
similarity index 100%
rename from viewpager2/api/restricted_1.0.0-alpha02.txt
rename to viewpager2/viewpager2/api/restricted_1.0.0-alpha02.txt
diff --git a/viewpager2/api/restricted_1.0.0-alpha03.txt b/viewpager2/viewpager2/api/restricted_1.0.0-alpha03.txt
similarity index 100%
rename from viewpager2/api/restricted_1.0.0-alpha03.txt
rename to viewpager2/viewpager2/api/restricted_1.0.0-alpha03.txt
diff --git a/viewpager2/api/restricted_1.0.0-alpha04.txt b/viewpager2/viewpager2/api/restricted_1.0.0-alpha04.txt
similarity index 100%
rename from viewpager2/api/restricted_1.0.0-alpha04.txt
rename to viewpager2/viewpager2/api/restricted_1.0.0-alpha04.txt
diff --git a/viewpager2/api/restricted_1.0.0-alpha05.txt b/viewpager2/viewpager2/api/restricted_1.0.0-alpha05.txt
similarity index 100%
rename from viewpager2/api/restricted_1.0.0-alpha05.txt
rename to viewpager2/viewpager2/api/restricted_1.0.0-alpha05.txt
diff --git a/viewpager2/api/restricted_1.0.0-alpha06.txt b/viewpager2/viewpager2/api/restricted_1.0.0-alpha06.txt
similarity index 100%
rename from viewpager2/api/restricted_1.0.0-alpha06.txt
rename to viewpager2/viewpager2/api/restricted_1.0.0-alpha06.txt
diff --git a/viewpager2/api/restricted_1.0.0-beta01.txt b/viewpager2/viewpager2/api/restricted_1.0.0-beta01.txt
similarity index 100%
rename from viewpager2/api/restricted_1.0.0-beta01.txt
rename to viewpager2/viewpager2/api/restricted_1.0.0-beta01.txt
diff --git a/viewpager2/api/restricted_1.0.0-beta02.txt b/viewpager2/viewpager2/api/restricted_1.0.0-beta02.txt
similarity index 100%
rename from viewpager2/api/restricted_1.0.0-beta02.txt
rename to viewpager2/viewpager2/api/restricted_1.0.0-beta02.txt
diff --git a/viewpager2/api/restricted_1.0.0-beta03.txt b/viewpager2/viewpager2/api/restricted_1.0.0-beta03.txt
similarity index 100%
rename from viewpager2/api/restricted_1.0.0-beta03.txt
rename to viewpager2/viewpager2/api/restricted_1.0.0-beta03.txt
diff --git a/viewpager2/api/restricted_1.0.0-beta04.txt b/viewpager2/viewpager2/api/restricted_1.0.0-beta04.txt
similarity index 100%
rename from viewpager2/api/restricted_1.0.0-beta04.txt
rename to viewpager2/viewpager2/api/restricted_1.0.0-beta04.txt
diff --git a/viewpager2/api/restricted_1.0.0-beta05.txt b/viewpager2/viewpager2/api/restricted_1.0.0-beta05.txt
similarity index 100%
rename from viewpager2/api/restricted_1.0.0-beta05.txt
rename to viewpager2/viewpager2/api/restricted_1.0.0-beta05.txt
diff --git a/viewpager2/api/restricted_1.0.0-beta06.txt b/viewpager2/viewpager2/api/restricted_1.0.0-beta06.txt
similarity index 100%
rename from viewpager2/api/restricted_1.0.0-beta06.txt
rename to viewpager2/viewpager2/api/restricted_1.0.0-beta06.txt
diff --git a/viewpager2/api/restricted_1.0.0-rc01.txt b/viewpager2/viewpager2/api/restricted_1.0.0-rc01.txt
similarity index 100%
rename from viewpager2/api/restricted_1.0.0-rc01.txt
rename to viewpager2/viewpager2/api/restricted_1.0.0-rc01.txt
diff --git a/viewpager2/api/restricted_1.1.0-alpha01.txt b/viewpager2/viewpager2/api/restricted_1.1.0-alpha01.txt
similarity index 100%
rename from viewpager2/api/restricted_1.1.0-alpha01.txt
rename to viewpager2/viewpager2/api/restricted_1.1.0-alpha01.txt
diff --git a/viewpager2/api/restricted_current.txt b/viewpager2/viewpager2/api/restricted_current.txt
similarity index 100%
rename from viewpager2/api/restricted_current.txt
rename to viewpager2/viewpager2/api/restricted_current.txt
diff --git a/viewpager2/build.gradle b/viewpager2/viewpager2/build.gradle
similarity index 100%
rename from viewpager2/build.gradle
rename to viewpager2/viewpager2/build.gradle
diff --git a/viewpager2/lint-baseline.xml b/viewpager2/viewpager2/lint-baseline.xml
similarity index 100%
rename from viewpager2/lint-baseline.xml
rename to viewpager2/viewpager2/lint-baseline.xml
diff --git a/viewpager2/src/androidTest/AndroidManifest.xml b/viewpager2/viewpager2/src/androidTest/AndroidManifest.xml
similarity index 100%
rename from viewpager2/src/androidTest/AndroidManifest.xml
rename to viewpager2/viewpager2/src/androidTest/AndroidManifest.xml
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/SparseAdapter.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/SparseAdapter.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/SparseAdapter.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/SparseAdapter.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/TouchConsumingTextView.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/TouchConsumingTextView.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/TouchConsumingTextView.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/TouchConsumingTextView.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AccessibilityTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AccessibilityTest.kt
similarity index 96%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/AccessibilityTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AccessibilityTest.kt
index 0945a5d..17c5a12 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AccessibilityTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AccessibilityTest.kt
@@ -66,7 +66,7 @@
     @SdkSuppress(minSdkVersion = 16)
     fun test_onPerformPageAction() {
         setUpTest(config.orientation).apply {
-            setAdapterSync(viewAdapterProvider(stringSequence(6)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(6)))
 
             val initialPage = viewPager.currentItem
             assertBasicState(initialPage)
@@ -93,7 +93,7 @@
     @Test
     fun test_collectionInfo() {
         setUpTest(config.orientation).apply {
-            setAdapterSync(viewAdapterProvider(stringSequence(6)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(6)))
 
             val initialPage = viewPager.currentItem
             assertBasicState(initialPage)
@@ -124,7 +124,7 @@
     @Test
     fun test_onOrientationChange() {
         setUpTest(config.orientation).apply {
-            setAdapterSync(viewAdapterProvider(stringSequence(2)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(2)))
 
             val initialPage = viewPager.currentItem
             assertBasicState(initialPage)
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeTest.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeTest.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeWhileSmoothScrollTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeWhileSmoothScrollTest.kt
similarity index 99%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeWhileSmoothScrollTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeWhileSmoothScrollTest.kt
index 3815b5a..847c44c 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeWhileSmoothScrollTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeWhileSmoothScrollTest.kt
@@ -102,7 +102,7 @@
         test = setUpTest(config.orientation)
         activityTestRule.runOnUiThread { test.viewPager.offscreenPageLimit = 1 }
         dataSet = stringSequence(pageCount).toMutableList()
-        test.setAdapterSync(config.adapterProvider(dataSet))
+        test.setAdapterSync(config.adapterProvider.provider(dataSet))
     }
 
     @Test
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterTest.kt
similarity index 98%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterTest.kt
index f2333f9..a61995a 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterTest.kt
@@ -49,7 +49,7 @@
     @Test
     fun test_setAdapter() {
         val recorder = test.viewPager.addNewRecordingCallback()
-        test.setAdapterSync(viewAdapterProvider(stringSequence(pageCount)))
+        test.setAdapterSync(viewAdapterProvider.provider(stringSequence(pageCount)))
         test.assertBasicState(0)
         test.viewPager.setCurrentItemSync(1, false, 2, SECONDS)
         test.assertBasicState(1)
@@ -195,7 +195,7 @@
 
     private fun setUpAdapterSync(pageCount: Int, initialPage: Int? = null) {
         dataSet = stringSequence(pageCount).toMutableList()
-        test.setAdapterSync(viewAdapterProvider(dataSet))
+        test.setAdapterSync(viewAdapterProvider.provider(dataSet))
 
         if (initialPage != null) {
             test.viewPager.setCurrentItemSync(initialPage, false, 2, SECONDS)
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
similarity index 93%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
index d724b7f..688625ca 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
@@ -599,9 +599,14 @@
 
 typealias AdapterProvider = (TestActivity) -> RecyclerView.Adapter<out RecyclerView.ViewHolder>
 
-typealias AdapterProviderForItems = (items: List<String>) -> AdapterProvider
+data class AdapterProviderForItems(
+    val name: String,
+    val provider: (items: List<String>) -> AdapterProvider
+) {
+    override fun toString(): String = name
+}
 
-val fragmentAdapterProvider: AdapterProviderForItems = { items ->
+val fragmentAdapterProvider = AdapterProviderForItems("fragmentAdapterProvider") { items ->
     { activity: TestActivity ->
         FragmentAdapter(
             activity.supportFragmentManager,
@@ -616,35 +621,38 @@
  * [FragmentStateAdapter.getItemId] and [FragmentStateAdapter.containsItem].
  * Not suitable for testing [RecyclerView.Adapter.notifyDataSetChanged].
  */
-val fragmentAdapterProviderCustomIds: AdapterProviderForItems = { items ->
-    { activity ->
-        fragmentAdapterProvider(items)(activity).also {
-            // more than position can represent, so a good test if ids are used consistently
-            val offset = 3L * Int.MAX_VALUE
-            val adapter = it as FragmentAdapter
-            adapter.positionToItemId = { position -> position + offset }
-            adapter.itemIdToContains = { itemId ->
-                val position = itemId - offset
-                position in (0 until adapter.itemCount)
+val fragmentAdapterProviderCustomIds =
+    AdapterProviderForItems("fragmentAdapterProviderCustomIds") { items ->
+        { activity ->
+            fragmentAdapterProvider.provider(items)(activity).also {
+                // more than position can represent, so a good test if ids are used consistently
+                val offset = 3L * Int.MAX_VALUE
+                val adapter = it as FragmentAdapter
+                adapter.positionToItemId = { position -> position + offset }
+                adapter.itemIdToContains = { itemId ->
+                    val position = itemId - offset
+                    position in (0 until adapter.itemCount)
+                }
             }
         }
     }
-}
 
 /**
  * Same as [fragmentAdapterProvider] but with a custom implementation of
  * [FragmentStateAdapter.getItemId] and [FragmentStateAdapter.containsItem].
  * Suitable for testing [RecyclerView.Adapter.notifyDataSetChanged].
  */
-val fragmentAdapterProviderValueId: AdapterProviderForItems = { items ->
-    { activity ->
-        fragmentAdapterProvider(items)(activity).also {
-            val adapter = it as FragmentAdapter
-            adapter.positionToItemId = { position -> items[position].getId() }
-            adapter.itemIdToContains = { itemId -> items.any { item -> item.getId() == itemId } }
+val fragmentAdapterProviderValueId =
+    AdapterProviderForItems("fragmentAdapterProviderValueId") { items ->
+        { activity ->
+            fragmentAdapterProvider.provider(items)(activity).also {
+                val adapter = it as FragmentAdapter
+                adapter.positionToItemId = { position -> items[position].getId() }
+                adapter.itemIdToContains =
+                    { itemId -> items.any { item -> item.getId() == itemId } }
+            }
         }
     }
-}
 
 /** Extracts the sole number from a [String] and converts it to a [Long] */
 private fun (String).getId(): Long {
@@ -660,9 +668,9 @@
  * [RecyclerView.Adapter.getItemId].
  * Suitable for testing [RecyclerView.Adapter.notifyDataSetChanged].mu
  */
-val viewAdapterProviderValueId: AdapterProviderForItems = { items ->
+val viewAdapterProviderValueId = AdapterProviderForItems("viewAdapterProviderValueId") { items ->
     { activity ->
-        viewAdapterProvider(items)(activity).also {
+        viewAdapterProvider.provider(items)(activity).also {
             val adapter = it as ViewAdapter
             adapter.positionToItemId = { position -> items[position].getId() }
             adapter.setHasStableIds(true)
@@ -670,7 +678,8 @@
     }
 }
 
-val viewAdapterProvider: AdapterProviderForItems = { items -> { ViewAdapter(items) } }
+val viewAdapterProvider =
+    AdapterProviderForItems("viewAdapterProvider") { items -> { ViewAdapter(items) } }
 
 fun stringSequence(pageCount: Int) = (0 until pageCount).map { it.toString() }
 
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BasicTest.java b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BasicTest.java
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/BasicTest.java
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BasicTest.java
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/CanScrollTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/CanScrollTest.kt
similarity index 88%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/CanScrollTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/CanScrollTest.kt
index e268559..045de41 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/CanScrollTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/CanScrollTest.kt
@@ -39,19 +39,19 @@
         // given
         setUpTest(ORIENTATION_HORIZONTAL).apply {
             // when no pages
-            setAdapterSync(viewAdapterProvider(stringSequence(0)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(0)))
 
             // then can't scroll
             assertScrollNone()
 
             // when 1 page
-            setAdapterSync(viewAdapterProvider(stringSequence(1)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(1)))
 
             // then can't scroll
             assertScrollNone()
 
             // when 2 pages
-            setAdapterSync(viewAdapterProvider(stringSequence(2)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(2)))
 
             // then can scroll right
             assertScrollRight()
@@ -84,19 +84,19 @@
         localeUtil.setLocale(LocaleTestUtils.RTL_LANGUAGE)
         setUpTest(ORIENTATION_HORIZONTAL).apply {
             // when no pages
-            setAdapterSync(viewAdapterProvider(stringSequence(0)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(0)))
 
             // then can't scroll
             assertScrollNone()
 
             // when 1 page
-            setAdapterSync(viewAdapterProvider(stringSequence(1)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(1)))
 
             // then can't scroll
             assertScrollNone()
 
             // when 2 pages
-            setAdapterSync(viewAdapterProvider(stringSequence(2)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(2)))
 
             // then can scroll left
             assertScrollLeft()
@@ -126,19 +126,19 @@
         // given
         setUpTest(ORIENTATION_VERTICAL).apply {
             // when no pages
-            setAdapterSync(viewAdapterProvider(stringSequence(0)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(0)))
 
             // then can't scroll
             assertScrollNone()
 
             // when 1 page
-            setAdapterSync(viewAdapterProvider(stringSequence(1)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(1)))
 
             // then can't scroll
             assertScrollNone()
 
             // when 2 pages
-            setAdapterSync(viewAdapterProvider(stringSequence(2)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(2)))
 
             // then can scroll down
             assertScrollDown()
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/ChangeDataSetWhileScrollingTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/ChangeDataSetWhileScrollingTest.kt
similarity index 98%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/ChangeDataSetWhileScrollingTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/ChangeDataSetWhileScrollingTest.kt
index 7f22960..7a30f81 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/ChangeDataSetWhileScrollingTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/ChangeDataSetWhileScrollingTest.kt
@@ -37,7 +37,7 @@
     fun test_regression01() {
         setUpTest(orientation).apply {
             val items = listOf("49", "51").toMutableList()
-            setAdapterSync(adapterProvider(items))
+            setAdapterSync(adapterProvider.provider(items))
             assertBasicState(0, items[0])
 
             viewPager.post {
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DisableUserInputTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DisableUserInputTest.kt
similarity index 93%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/DisableUserInputTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DisableUserInputTest.kt
index fb2abd3..97443aa 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DisableUserInputTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DisableUserInputTest.kt
@@ -61,21 +61,22 @@
     private lateinit var test: Context
     private lateinit var adapterProvider: AdapterProvider
 
-    private val touchConsumingViewAdapter: AdapterProviderForItems = { items ->
-        {
-            object : ViewAdapter(items) {
-                override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
-                    super.onBindViewHolder(holder, position)
-                    (holder.itemView as TouchConsumingTextView).consumeTouches =
+    private val touchConsumingViewAdapter =
+        AdapterProviderForItems("touchConsumingViewAdapter") { items ->
+            {
+                object : ViewAdapter(items) {
+                    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+                        super.onBindViewHolder(holder, position)
+                        (holder.itemView as TouchConsumingTextView).consumeTouches =
                             config.childViewConsumesTouches
+                    }
                 }
             }
         }
-    }
 
     override fun setUp() {
         super.setUp()
-        adapterProvider = touchConsumingViewAdapter(stringSequence(pageCount))
+        adapterProvider = touchConsumingViewAdapter.provider(stringSequence(pageCount))
         test = setUpTest(config.orientation).also {
             it.viewPager.isUserInputEnabled = false
             it.setAdapterSync(adapterProvider)
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DragWhileSmoothScrollTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DragWhileSmoothScrollTest.kt
similarity index 99%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/DragWhileSmoothScrollTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DragWhileSmoothScrollTest.kt
index 0446457..5b9266c 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DragWhileSmoothScrollTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DragWhileSmoothScrollTest.kt
@@ -73,7 +73,7 @@
         assertThat(config.distanceToTargetWhenStartDrag, greaterThan(0f))
         val pageCount = max(config.startPage, config.targetPage) + 1
         test = setUpTest(config.orientation)
-        test.setAdapterSync(viewAdapterProvider(stringSequence(pageCount)))
+        test.setAdapterSync(viewAdapterProvider.provider(stringSequence(pageCount)))
         test.viewPager.setCurrentItemSync(config.startPage, false, 2, SECONDS)
 
         var recorder = test.viewPager.addNewRecordingCallback()
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/EditTextFocusTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/EditTextFocusTest.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/EditTextFocusTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/EditTextFocusTest.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
similarity index 99%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
index eb6eb42..6ee9599 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
@@ -101,7 +101,7 @@
             localeUtil.resetLocale()
             localeUtil.setLocale(LocaleTestUtils.RTL_LANGUAGE)
         }
-        adapterProvider = viewAdapterProvider(stringSequence(pageCount))
+        adapterProvider = viewAdapterProvider.provider(stringSequence(pageCount))
         test = setUpTest(config.orientation).also {
             fakeDragger = PageSwiperFakeDrag(it.viewPager) { it.viewPager.pageSize }
             it.viewPager.isUserInputEnabled = config.enableUserInput
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentLifecycleTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentLifecycleTest.kt
similarity index 97%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentLifecycleTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentLifecycleTest.kt
index 51f84084..71064fd 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentLifecycleTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentLifecycleTest.kt
@@ -57,7 +57,7 @@
     fun test_swipeBetweenPages() {
         setUpTest(orientation).apply {
             val expectedValues = stringSequence(totalPages).toMutableList()
-            val adapter = adapterProvider(expectedValues.toList()) // immutable defensive copy
+            val adapter = adapterProvider.provider(expectedValues.toList()) // defensive copy
             setAdapterSync(adapter)
 
             var ix = 0
@@ -86,7 +86,7 @@
     fun test_setCurrentItem() {
         setUpTest(orientation).apply {
             val expectedValues = stringSequence(totalPages).toMutableList()
-            val adapter = adapterProvider(expectedValues.toList()) // immutable defensive copy
+            val adapter = adapterProvider.provider(expectedValues.toList()) // defensive copy
             setAdapterSync(adapter)
 
             performAssertions(expectedValues, 0)
@@ -114,7 +114,7 @@
     fun test_dataSetChange() {
         setUpTest(orientation).apply {
             val items = stringSequence(totalPages).toMutableList()
-            setAdapterSync(adapterProvider(items))
+            setAdapterSync(adapterProvider.provider(items))
             val adapter = viewPager.adapter!!
 
             performAssertions(items, 0)
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/HostFragmentBackStackTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/HostFragmentBackStackTest.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/HostFragmentBackStackTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/HostFragmentBackStackTest.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/ItemDecorationTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/ItemDecorationTest.kt
similarity index 98%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/ItemDecorationTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/ItemDecorationTest.kt
index a46284a..ed066f6 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/ItemDecorationTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/ItemDecorationTest.kt
@@ -61,7 +61,7 @@
     ) {
         // given
         setUpTest(orientation).run {
-            setAdapterSync(adapterProvider(stringSequence(3)))
+            setAdapterSync(adapterProvider.provider(stringSequence(3)))
 
             // sanity checks
             assertBasicState(0)
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/MutableCollectionsTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/MutableCollectionsTest.kt
similarity index 99%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/MutableCollectionsTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/MutableCollectionsTest.kt
index ee8c165..c0840ce 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/MutableCollectionsTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/MutableCollectionsTest.kt
@@ -49,7 +49,7 @@
     fun test() {
         testConfig.apply {
             setUpTest(orientation).apply {
-                setAdapterSync(adapterProvider(items))
+                setAdapterSync(adapterProvider.provider(items))
 
                 verifyViewPagerContent(items) // sanity check
 
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt
similarity index 98%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt
index de156e2..2920809 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt
@@ -82,7 +82,7 @@
             test.viewPager.offscreenPageLimit = config.offscreenPageLimit
         }
         val recorder = test.viewPager.addNewRecordingCallback()
-        test.setAdapterSync(config.adapterProvider(stringSequence(pageCount)))
+        test.setAdapterSync(config.adapterProvider.provider(stringSequence(pageCount)))
         // Do not perform self check (which checks number of shown + cached fragments) in
         // this test, as that check is not valid in the presence of offscreen page limit
         test.assertBasicState(firstPage, performSelfCheck = false)
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OrientationTest.java b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OrientationTest.java
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/OrientationTest.java
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OrientationTest.java
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PaddingMarginDecorationTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PaddingMarginDecorationTest.kt
similarity index 96%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/PaddingMarginDecorationTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PaddingMarginDecorationTest.kt
index 666bbb0..82f3535 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PaddingMarginDecorationTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PaddingMarginDecorationTest.kt
@@ -133,12 +133,12 @@
     }
 
     private val adapterProvider: AdapterProviderForItems get() {
-        return if (config.itemMarginPx > 0) {
-            { items -> { MarginViewAdapter(config.itemMarginPx, items) } }
-        } else {
-            { items -> { ViewAdapter(items) } }
+            return AdapterProviderForItems("adapterProvider", if (config.itemMarginPx > 0) {
+                { items -> { MarginViewAdapter(config.itemMarginPx, items) } }
+            } else {
+                { items -> { ViewAdapter(items) } }
+            })
         }
-    }
 
     class MarginViewAdapter(private val margin: Int, items: List<String>) : ViewAdapter(items) {
         override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
@@ -186,7 +186,7 @@
 
     @Test
     fun test_pageSize() {
-        test.setAdapterSync(adapterProvider(stringSequence(1)))
+        test.setAdapterSync(adapterProvider.provider(stringSequence(1)))
 
         val f = if (viewPager.isHorizontal) fLeft + fRight else fTop + fBottom
 
@@ -232,7 +232,7 @@
      */
     @Test
     fun test_swipeBetweenPages() {
-        test.setAdapterSync(adapterProvider(stringSequence(2)))
+        test.setAdapterSync(adapterProvider.provider(stringSequence(2)))
         listOf(1, 0).forEach { targetPage ->
             // given
             val initialPage = viewPager.currentItem
@@ -292,7 +292,7 @@
         val totalPages = 2
         val edgePages = setOf(0, totalPages - 1)
 
-        test.setAdapterSync(adapterProvider(stringSequence(totalPages)))
+        test.setAdapterSync(adapterProvider.provider(stringSequence(totalPages)))
         listOf(0, 1, 1).forEach { targetPage ->
             // given
             val initialPage = viewPager.currentItem
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
similarity index 97%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
index cc4bb92..fe9b258 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
@@ -109,7 +109,7 @@
     @Test
     fun test_swipeBetweenPages() {
         setUpTest(config.orientation).apply {
-            setAdapterSync(viewAdapterProvider(stringSequence(4)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(4)))
             listOf(1, 2, 3, 2, 1, 0).forEach { targetPage ->
                 // given
                 val initialPage = viewPager.currentItem
@@ -172,7 +172,7 @@
 
         setUpTest(config.orientation).apply {
 
-            setAdapterSync(viewAdapterProvider(stringSequence(totalPages)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(totalPages)))
             listOf(0, 0, 1, 2, 2, 2, 1, 2, 2, 2, 1, 0, 0, 0).forEach { targetPage ->
                 // given
                 val initialPage = viewPager.currentItem
@@ -235,7 +235,7 @@
     fun test_peekOnAdjacentPage_next() {
         // given
         setUpTest(config.orientation).apply {
-            setAdapterSync(viewAdapterProvider(stringSequence(3)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(3)))
             val callback = viewPager.addNewRecordingCallback()
             val latch = viewPager.addWaitForScrolledLatch(0)
 
@@ -294,7 +294,7 @@
     fun test_peekOnAdjacentPage_previous() {
         // given
         setUpTest(config.orientation).apply {
-            setAdapterSync(viewAdapterProvider(stringSequence(3)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(3)))
 
             viewPager.setCurrentItemSync(2, false, 1, SECONDS)
 
@@ -371,7 +371,7 @@
     fun test_selectItemProgrammatically_smoothScroll() {
         // given
         setUpTest(config.orientation).apply {
-            setAdapterSync(viewAdapterProvider(stringSequence(1000)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(1000)))
 
             // when
             listOf(6, 5, 6, 3, 10, 0, 0, 999, 999, 0).forEach { targetPage ->
@@ -412,7 +412,7 @@
     fun test_multiplePageChanges() {
         // given
         setUpTest(config.orientation).apply {
-            setAdapterSync(viewAdapterProvider(stringSequence(10)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(10)))
             val targetPages = listOf(4, 9)
             val callback = viewPager.addNewRecordingCallback()
             val latch = viewPager.addWaitForScrolledLatch(targetPages.last(), true)
@@ -462,7 +462,7 @@
     fun test_noSmoothScroll_after_smoothScroll() {
         // given
         setUpTest(config.orientation).apply {
-            setAdapterSync(viewAdapterProvider(stringSequence(6)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(6)))
             val targetPage = 4
             val marker = 1
             val callback = viewPager.addNewRecordingCallback()
@@ -580,7 +580,7 @@
         // given
         assertThat(targetPage, greaterThanOrEqualTo(4))
         setUpTest(config.orientation).apply {
-            val adapterProvider = viewAdapterProvider(stringSequence(5))
+            val adapterProvider = viewAdapterProvider.provider(stringSequence(5))
             setAdapterSync(adapterProvider)
             val marker = 1
             val callback = viewPager.addNewRecordingCallback()
@@ -640,7 +640,7 @@
     fun test_selectItemProgrammatically_noSmoothScroll() {
         // given
         setUpTest(config.orientation).apply {
-            setAdapterSync(viewAdapterProvider(stringSequence(3)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(3)))
 
             // when
             listOf(2, 2, 0, 0, 1, 2, 1, 0).forEach { targetPage ->
@@ -672,7 +672,7 @@
     fun test_swipeReleaseSwipeBack() {
         // given
         val test = setUpTest(config.orientation)
-        test.setAdapterSync(viewAdapterProvider(stringSequence(3)))
+        test.setAdapterSync(viewAdapterProvider.provider(stringSequence(3)))
         val currentPage = test.viewPager.currentItem
         val halfPage = test.viewPager.pageSize / 2f
         val pageSwiper = PageSwiperManual(test.viewPager)
@@ -746,7 +746,7 @@
     private fun test_selectItemProgrammatically_noCallback(smoothScroll: Boolean) {
         // given
         setUpTest(config.orientation).apply {
-            setAdapterSync(viewAdapterProvider(stringSequence(3)))
+            setAdapterSync(viewAdapterProvider.provider(stringSequence(3)))
 
             // when
             listOf(2, 2, 0, 0, 1, 2, 1, 0).forEach { targetPage ->
@@ -788,7 +788,7 @@
     @Test
     fun test_getScrollState() {
         val test = setUpTest(config.orientation)
-        test.setAdapterSync(viewAdapterProvider(stringSequence(5)))
+        test.setAdapterSync(viewAdapterProvider.provider(stringSequence(5)))
 
         // Test SCROLL_STATE_SETTLING
         test_getScrollState(test, SCROLL_STATE_SETTLING, 1) {
@@ -852,7 +852,7 @@
         // given
         val test = setUpTest(config.orientation)
         val recorder = test.viewPager.addNewRecordingCallback()
-        val adapterProvider = viewAdapterProvider(stringSequence(3))
+        val adapterProvider = viewAdapterProvider.provider(stringSequence(3))
         val marker = 1
 
         fun expectedEvents(page: Int): List<Event> {
@@ -901,7 +901,7 @@
     private fun test_setCurrentItem_outOfBounds(smoothScroll: Boolean) {
         val test = setUpTest(config.orientation)
         val n = 3
-        test.setAdapterSync(viewAdapterProvider(stringSequence(n)))
+        test.setAdapterSync(viewAdapterProvider.provider(stringSequence(n)))
         val adapterCount = test.viewPager.adapter!!.itemCount
 
         listOf(-5, -1, n, n + 1, adapterCount, adapterCount + 1).forEach { targetPage ->
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageFillTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageFillTest.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageFillTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageFillTest.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerItemAnimatorTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerItemAnimatorTest.kt
similarity index 97%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerItemAnimatorTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerItemAnimatorTest.kt
index 8dc6458..b0504d0 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerItemAnimatorTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerItemAnimatorTest.kt
@@ -42,7 +42,7 @@
     @Test
     fun test() {
         setUpTest(ORIENTATION_HORIZONTAL).apply {
-            setAdapterSync(viewAdapterProviderValueId(stringSequence(5)))
+            setAdapterSync(viewAdapterProviderValueId.provider(stringSequence(5)))
             assertBasicState(0)
 
             val rv = viewPager.getChildAt(0) as RecyclerView
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt
similarity index 99%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt
index 4d78da2..85ece6c 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt
@@ -125,7 +125,7 @@
     fun test() {
         // given
         val test = setUpTest(config.orientation)
-        test.setAdapterSync(viewAdapterProvider(stringSequence(100)))
+        test.setAdapterSync(viewAdapterProvider.provider(stringSequence(100)))
 
         // when
         config.pageList.forEach { targetPage ->
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt
similarity index 99%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt
index 8d6159b..e45e4d9 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt
@@ -138,7 +138,7 @@
         config.apply {
             // given
             setUpTest(orientation).apply {
-                setAdapterSync(viewAdapterProvider(stringSequence(totalPages)))
+                setAdapterSync(viewAdapterProvider.provider(stringSequence(totalPages)))
                 val callback = viewPager.addNewRecordingCallback()
                 var currentPage = viewPager.currentItem
 
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SwipeTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SwipeTest.kt
similarity index 98%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/SwipeTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SwipeTest.kt
index 9cacdcd..91bf776 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SwipeTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SwipeTest.kt
@@ -40,7 +40,7 @@
         testConfig.apply {
             setUpTest(orientation).apply {
                 val expectedValues = stringSequence(totalPages).toMutableList()
-                val adapter = adapterProvider(expectedValues.toList()) // immutable defensive copy
+                val adapter = adapterProvider.provider(expectedValues.toList()) // defensive copy
                 setAdapterSync(adapter)
                 assertBasicState(0)
 
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt
similarity index 97%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt
index ebe5a6a..cb29e66 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt
+++ b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt
@@ -45,7 +45,7 @@
     fun test_swipeBetweenPages() {
         setUpTest(orientation).apply {
             val expectedValues = stringSequence(totalPages)
-            val adapter = adapterProvider(expectedValues)
+            val adapter = adapterProvider.provider(expectedValues)
 
             val fragmentManager = activity.supportFragmentManager
 
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/BaseActivity.java b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/BaseActivity.java
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/BaseActivity.java
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/BaseActivity.java
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/FragmentAdapter.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/FragmentAdapter.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/FragmentAdapter.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/FragmentAdapter.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/ManualSwipeInjector.java b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/ManualSwipeInjector.java
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/ManualSwipeInjector.java
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/ManualSwipeInjector.java
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiper.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiper.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiper.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiper.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiperEspresso.java b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiperEspresso.java
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiperEspresso.java
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiperEspresso.java
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiperFakeDrag.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiperFakeDrag.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiperFakeDrag.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiperFakeDrag.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiperManual.java b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiperManual.java
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiperManual.java
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageSwiperManual.java
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageView.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageView.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageView.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageView.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/SelfChecking.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/SelfChecking.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/SelfChecking.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/SelfChecking.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/TestActivity.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/TestActivity.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/TestActivity.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/TestActivity.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/TranslatedCoordinatesProvider.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/TranslatedCoordinatesProvider.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/TranslatedCoordinatesProvider.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/TranslatedCoordinatesProvider.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/ViewAdapter.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/ViewAdapter.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/ViewAdapter.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/ViewAdapter.kt
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/WaitForInjectMotionEventsAction.kt b/viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/WaitForInjectMotionEventsAction.kt
similarity index 100%
rename from viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/WaitForInjectMotionEventsAction.kt
rename to viewpager2/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/WaitForInjectMotionEventsAction.kt
diff --git a/viewpager2/src/androidTest/res/layout/activity_test_layout.xml b/viewpager2/viewpager2/src/androidTest/res/layout/activity_test_layout.xml
similarity index 100%
rename from viewpager2/src/androidTest/res/layout/activity_test_layout.xml
rename to viewpager2/viewpager2/src/androidTest/res/layout/activity_test_layout.xml
diff --git a/viewpager2/src/androidTest/res/layout/item_test_layout.xml b/viewpager2/viewpager2/src/androidTest/res/layout/item_test_layout.xml
similarity index 100%
rename from viewpager2/src/androidTest/res/layout/item_test_layout.xml
rename to viewpager2/viewpager2/src/androidTest/res/layout/item_test_layout.xml
diff --git a/viewpager2/src/androidTest/res/layout/orientation_default.xml b/viewpager2/viewpager2/src/androidTest/res/layout/orientation_default.xml
similarity index 100%
rename from viewpager2/src/androidTest/res/layout/orientation_default.xml
rename to viewpager2/viewpager2/src/androidTest/res/layout/orientation_default.xml
diff --git a/viewpager2/src/androidTest/res/layout/orientation_horizontal.xml b/viewpager2/viewpager2/src/androidTest/res/layout/orientation_horizontal.xml
similarity index 100%
rename from viewpager2/src/androidTest/res/layout/orientation_horizontal.xml
rename to viewpager2/viewpager2/src/androidTest/res/layout/orientation_horizontal.xml
diff --git a/viewpager2/src/androidTest/res/layout/orientation_vertical.xml b/viewpager2/viewpager2/src/androidTest/res/layout/orientation_vertical.xml
similarity index 100%
rename from viewpager2/src/androidTest/res/layout/orientation_vertical.xml
rename to viewpager2/viewpager2/src/androidTest/res/layout/orientation_vertical.xml
diff --git a/viewpager2/src/androidTest/res/layout/vertical_scrollbars.xml b/viewpager2/viewpager2/src/androidTest/res/layout/vertical_scrollbars.xml
similarity index 100%
rename from viewpager2/src/androidTest/res/layout/vertical_scrollbars.xml
rename to viewpager2/viewpager2/src/androidTest/res/layout/vertical_scrollbars.xml
diff --git a/viewpager2/src/main/AndroidManifest.xml b/viewpager2/viewpager2/src/main/AndroidManifest.xml
similarity index 100%
rename from viewpager2/src/main/AndroidManifest.xml
rename to viewpager2/viewpager2/src/main/AndroidManifest.xml
diff --git a/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
similarity index 99%
rename from viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
rename to viewpager2/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
index b40f192..a616e861 100644
--- a/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
+++ b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
@@ -473,7 +473,6 @@
     /**
      * Default implementation works for collections that don't add, move, remove items.
      * <p>
-     * TODO(b/122670460): add lint rule
      * When overriding, also override {@link #containsItem(long)}.
      * <p>
      * If the item is not a part of the collection, return {@link RecyclerView#NO_ID}.
@@ -481,6 +480,7 @@
      * @param position Adapter position
      * @return stable item id {@link RecyclerView.Adapter#hasStableIds()}
      */
+    // TODO(b/122670460): add lint rule
     @Override
     public long getItemId(int position) {
         return position;
@@ -489,9 +489,9 @@
     /**
      * Default implementation works for collections that don't add, move, remove items.
      * <p>
-     * TODO(b/122670460): add lint rule
      * When overriding, also override {@link #getItemId(int)}
      */
+    // TODO(b/122670460): add lint rule
     public boolean containsItem(long itemId) {
         return itemId >= 0 && itemId < getItemCount();
     }
diff --git a/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentViewHolder.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentViewHolder.java
similarity index 100%
rename from viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentViewHolder.java
rename to viewpager2/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentViewHolder.java
diff --git a/viewpager2/src/main/java/androidx/viewpager2/adapter/StatefulAdapter.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/adapter/StatefulAdapter.java
similarity index 100%
rename from viewpager2/src/main/java/androidx/viewpager2/adapter/StatefulAdapter.java
rename to viewpager2/viewpager2/src/main/java/androidx/viewpager2/adapter/StatefulAdapter.java
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/AnimateLayoutChangeDetector.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/AnimateLayoutChangeDetector.java
similarity index 100%
rename from viewpager2/src/main/java/androidx/viewpager2/widget/AnimateLayoutChangeDetector.java
rename to viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/AnimateLayoutChangeDetector.java
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/CompositeOnPageChangeCallback.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/CompositeOnPageChangeCallback.java
similarity index 100%
rename from viewpager2/src/main/java/androidx/viewpager2/widget/CompositeOnPageChangeCallback.java
rename to viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/CompositeOnPageChangeCallback.java
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/CompositePageTransformer.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/CompositePageTransformer.java
similarity index 100%
rename from viewpager2/src/main/java/androidx/viewpager2/widget/CompositePageTransformer.java
rename to viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/CompositePageTransformer.java
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/FakeDrag.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/FakeDrag.java
similarity index 100%
rename from viewpager2/src/main/java/androidx/viewpager2/widget/FakeDrag.java
rename to viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/FakeDrag.java
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/MarginPageTransformer.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/MarginPageTransformer.java
similarity index 100%
rename from viewpager2/src/main/java/androidx/viewpager2/widget/MarginPageTransformer.java
rename to viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/MarginPageTransformer.java
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/PageTransformerAdapter.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/PageTransformerAdapter.java
similarity index 100%
rename from viewpager2/src/main/java/androidx/viewpager2/widget/PageTransformerAdapter.java
rename to viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/PageTransformerAdapter.java
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java
similarity index 100%
rename from viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java
rename to viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
similarity index 99%
rename from viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
rename to viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
index e69087b..32bdc64 100644
--- a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
+++ b/viewpager2/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
@@ -582,8 +582,6 @@
      * the current item and the specified item. Silently ignored if the adapter is not set or
      * empty. Clamps item to the bounds of the adapter.
      *
-     * TODO(b/123069219): verify first layout behavior
-     *
      * @param item Item index to select
      */
     public void setCurrentItem(int item) {
diff --git a/viewpager2/src/main/res/values/attrs.xml b/viewpager2/viewpager2/src/main/res/values/attrs.xml
similarity index 100%
rename from viewpager2/src/main/res/values/attrs.xml
rename to viewpager2/viewpager2/src/main/res/values/attrs.xml
diff --git a/work/workmanager-benchmark/build.gradle b/work/workmanager-benchmark/build.gradle
index 8dd3ba4..4c74f21 100644
--- a/work/workmanager-benchmark/build.gradle
+++ b/work/workmanager-benchmark/build.gradle
@@ -32,7 +32,7 @@
 dependencies {
     androidTestImplementation(project(':work:work-runtime-ktx'))
     androidTestImplementation(project(":benchmark:benchmark-junit4"))
-    androidTestImplementation("androidx.room:room-runtime:2.2.2")
+    androidTestImplementation("androidx.room:room-runtime:2.2.3")
     androidTestImplementation(JUNIT)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/work/workmanager-gcm/build.gradle b/work/workmanager-gcm/build.gradle
index f960dec..e7f4a95 100644
--- a/work/workmanager-gcm/build.gradle
+++ b/work/workmanager-gcm/build.gradle
@@ -51,9 +51,9 @@
         implementation(project(":room:room-runtime"))
         androidTestImplementation(project(":room:room-testing"))
     } else {
-        annotationProcessor("androidx.room:room-compiler:2.2.2")
-        implementation("androidx.room:room-runtime:2.2.2")
-        androidTestImplementation("androidx.room:room-testing:2.2.2")
+        annotationProcessor("androidx.room:room-compiler:2.2.3")
+        implementation("androidx.room:room-runtime:2.2.3")
+        androidTestImplementation("androidx.room:room-testing:2.2.3")
     }
 
     androidTestImplementation(project(":work:work-runtime-ktx"))
diff --git a/work/workmanager-ktx/build.gradle b/work/workmanager-ktx/build.gradle
index e515cec..25ad0d8c 100644
--- a/work/workmanager-ktx/build.gradle
+++ b/work/workmanager-ktx/build.gradle
@@ -53,7 +53,7 @@
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
-    androidTestImplementation("androidx.room:room-testing:2.2.2")
+    androidTestImplementation("androidx.room:room-testing:2.2.3")
     testImplementation(JUNIT)
 }
 
diff --git a/work/workmanager-testing/build.gradle b/work/workmanager-testing/build.gradle
index 84b62ad..42f84b7 100644
--- a/work/workmanager-testing/build.gradle
+++ b/work/workmanager-testing/build.gradle
@@ -38,7 +38,7 @@
 dependencies {
     api(project(':work:work-runtime-ktx'))
     implementation("androidx.lifecycle:lifecycle-livedata-core:2.1.0")
-    implementation("androidx.room:room-runtime:2.2.2")
+    implementation("androidx.room:room-runtime:2.2.3")
 
     androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/work/workmanager/api/2.4.0-alpha01.txt b/work/workmanager/api/2.4.0-alpha01.txt
index 0ecdec1..105417e 100644
--- a/work/workmanager/api/2.4.0-alpha01.txt
+++ b/work/workmanager/api/2.4.0-alpha01.txt
@@ -120,6 +120,7 @@
 
   public enum ExistingWorkPolicy {
     enum_constant public static final androidx.work.ExistingWorkPolicy APPEND;
+    enum_constant public static final androidx.work.ExistingWorkPolicy APPEND_OR_REPLACE;
     enum_constant public static final androidx.work.ExistingWorkPolicy KEEP;
     enum_constant public static final androidx.work.ExistingWorkPolicy REPLACE;
   }
diff --git a/work/workmanager/api/current.txt b/work/workmanager/api/current.txt
index 0ecdec1..105417e 100644
--- a/work/workmanager/api/current.txt
+++ b/work/workmanager/api/current.txt
@@ -120,6 +120,7 @@
 
   public enum ExistingWorkPolicy {
     enum_constant public static final androidx.work.ExistingWorkPolicy APPEND;
+    enum_constant public static final androidx.work.ExistingWorkPolicy APPEND_OR_REPLACE;
     enum_constant public static final androidx.work.ExistingWorkPolicy KEEP;
     enum_constant public static final androidx.work.ExistingWorkPolicy REPLACE;
   }
diff --git a/work/workmanager/api/public_plus_experimental_2.4.0-alpha01.txt b/work/workmanager/api/public_plus_experimental_2.4.0-alpha01.txt
index 0ecdec1..105417e 100644
--- a/work/workmanager/api/public_plus_experimental_2.4.0-alpha01.txt
+++ b/work/workmanager/api/public_plus_experimental_2.4.0-alpha01.txt
@@ -120,6 +120,7 @@
 
   public enum ExistingWorkPolicy {
     enum_constant public static final androidx.work.ExistingWorkPolicy APPEND;
+    enum_constant public static final androidx.work.ExistingWorkPolicy APPEND_OR_REPLACE;
     enum_constant public static final androidx.work.ExistingWorkPolicy KEEP;
     enum_constant public static final androidx.work.ExistingWorkPolicy REPLACE;
   }
diff --git a/work/workmanager/api/public_plus_experimental_current.txt b/work/workmanager/api/public_plus_experimental_current.txt
index 0ecdec1..105417e 100644
--- a/work/workmanager/api/public_plus_experimental_current.txt
+++ b/work/workmanager/api/public_plus_experimental_current.txt
@@ -120,6 +120,7 @@
 
   public enum ExistingWorkPolicy {
     enum_constant public static final androidx.work.ExistingWorkPolicy APPEND;
+    enum_constant public static final androidx.work.ExistingWorkPolicy APPEND_OR_REPLACE;
     enum_constant public static final androidx.work.ExistingWorkPolicy KEEP;
     enum_constant public static final androidx.work.ExistingWorkPolicy REPLACE;
   }
diff --git a/work/workmanager/build.gradle b/work/workmanager/build.gradle
index 1c15e53..48a698e 100644
--- a/work/workmanager/build.gradle
+++ b/work/workmanager/build.gradle
@@ -62,9 +62,11 @@
 }
 
 dependencies {
-    annotationProcessor("androidx.room:room-compiler:2.2.2")
-    implementation("androidx.room:room-runtime:2.2.2")
-    androidTestImplementation("androidx.room:room-testing:2.2.2")
+    annotationProcessor("androidx.room:room-compiler:2.2.3")
+    implementation("androidx.room:room-runtime:2.2.3")
+    androidTestImplementation("androidx.room:room-testing:2.2.3")
+    implementation("androidx.sqlite:sqlite:2.1.0-rc01")
+    implementation("androidx.sqlite:sqlite-framework:2.1.0-rc01")
     api(GUAVA_LISTENABLE_FUTURE)
     api("androidx.lifecycle:lifecycle-livedata:2.1.0")
     implementation("androidx.core:core:1.1.0")
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
index f7b9e13..32f50ab 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/WorkManagerImplTest.java
@@ -17,6 +17,7 @@
 package androidx.work.impl;
 
 import static androidx.work.ExistingWorkPolicy.APPEND;
+import static androidx.work.ExistingWorkPolicy.APPEND_OR_REPLACE;
 import static androidx.work.ExistingWorkPolicy.KEEP;
 import static androidx.work.ExistingWorkPolicy.REPLACE;
 import static androidx.work.NetworkType.METERED;
@@ -908,6 +909,110 @@
 
     @Test
     @MediumTest
+    public void testBeginUniqueWork_insertsWithAppendOrReplaceWithFailedPreRequisites()
+            throws ExecutionException, InterruptedException {
+
+        when(mWorkManagerImpl.getSchedulers()).thenReturn(Collections.<Scheduler>emptyList());
+        final String uniqueName = "myname";
+        OneTimeWorkRequest preRequisiteRequest = new OneTimeWorkRequest.Builder(TestWorker.class)
+                .setInitialState(FAILED)
+                .build();
+
+        mWorkManagerImpl.beginUniqueWork(uniqueName, APPEND_OR_REPLACE, preRequisiteRequest)
+                .enqueue()
+                .getResult()
+                .get();
+
+        OneTimeWorkRequest appendRequest = new OneTimeWorkRequest.Builder(TestWorker.class)
+                .build();
+
+        mWorkManagerImpl.beginUniqueWork(uniqueName, APPEND_OR_REPLACE, appendRequest)
+                .enqueue()
+                .getResult()
+                .get();
+
+        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
+        DependencyDao dependencyDao = mDatabase.dependencyDao();
+
+        WorkSpec deleted = workSpecDao.getWorkSpec(preRequisiteRequest.getStringId());
+        assertThat(deleted, is(nullValue()));
+        WorkSpec appended = workSpecDao.getWorkSpec(appendRequest.getStringId());
+        List<String> preRequisites = dependencyDao.getPrerequisites(appendRequest.getStringId());
+        assertThat(appended.state, is(ENQUEUED));
+        assertThat(preRequisites.size(), is(0));
+    }
+
+    @Test
+    @MediumTest
+    public void testBeginUniqueWork_insertsWithAppendOrReplaceWithCancelledPreRequisites()
+            throws ExecutionException, InterruptedException {
+
+        when(mWorkManagerImpl.getSchedulers()).thenReturn(Collections.<Scheduler>emptyList());
+        final String uniqueName = "myname";
+        OneTimeWorkRequest preRequisiteRequest = new OneTimeWorkRequest.Builder(TestWorker.class)
+                .setInitialState(CANCELLED)
+                .build();
+
+        mWorkManagerImpl.beginUniqueWork(uniqueName, APPEND_OR_REPLACE, preRequisiteRequest)
+                .enqueue()
+                .getResult()
+                .get();
+
+        OneTimeWorkRequest appendRequest = new OneTimeWorkRequest.Builder(TestWorker.class)
+                .build();
+
+        mWorkManagerImpl.beginUniqueWork(uniqueName, APPEND_OR_REPLACE, appendRequest)
+                .enqueue()
+                .getResult()
+                .get();
+
+        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
+        DependencyDao dependencyDao = mDatabase.dependencyDao();
+
+        WorkSpec deleted = workSpecDao.getWorkSpec(preRequisiteRequest.getStringId());
+        assertThat(deleted, is(nullValue()));
+        WorkSpec appended = workSpecDao.getWorkSpec(appendRequest.getStringId());
+        List<String> preRequisites = dependencyDao.getPrerequisites(appendRequest.getStringId());
+        assertThat(appended.state, is(ENQUEUED));
+        assertThat(preRequisites.size(), is(0));
+    }
+
+    @Test
+    @MediumTest
+    public void testBeginUniqueWork_appendsWork_whenUsingAppendOrReplace()
+            throws ExecutionException, InterruptedException {
+
+        when(mWorkManagerImpl.getSchedulers()).thenReturn(Collections.<Scheduler>emptyList());
+        final String uniqueName = "myname";
+        OneTimeWorkRequest preRequisiteRequest = new OneTimeWorkRequest.Builder(TestWorker.class)
+                .build();
+
+        mWorkManagerImpl.beginUniqueWork(uniqueName, APPEND_OR_REPLACE, preRequisiteRequest)
+                .enqueue()
+                .getResult()
+                .get();
+
+        OneTimeWorkRequest appendRequest = new OneTimeWorkRequest.Builder(TestWorker.class)
+                .build();
+
+        mWorkManagerImpl.beginUniqueWork(uniqueName, APPEND_OR_REPLACE, appendRequest)
+                .enqueue()
+                .getResult()
+                .get();
+
+        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
+        DependencyDao dependencyDao = mDatabase.dependencyDao();
+
+        WorkSpec preRequisite = workSpecDao.getWorkSpec(preRequisiteRequest.getStringId());
+        WorkSpec appended = workSpecDao.getWorkSpec(appendRequest.getStringId());
+        List<String> preRequisites = dependencyDao.getPrerequisites(appendRequest.getStringId());
+        assertThat(appended.state, is(BLOCKED));
+        assertThat(preRequisites.size(), is(1));
+        assertThat(preRequisites, containsInAnyOrder(preRequisite.id));
+    }
+
+    @Test
+    @MediumTest
     public void testBeginUniqueWork_insertsExistingWorkWhenNothingToAppendTo()
             throws ExecutionException, InterruptedException {
 
diff --git a/work/workmanager/src/main/java/androidx/work/Data.java b/work/workmanager/src/main/java/androidx/work/Data.java
index f18dfd6..19f52f2 100644
--- a/work/workmanager/src/main/java/androidx/work/Data.java
+++ b/work/workmanager/src/main/java/androidx/work/Data.java
@@ -343,9 +343,8 @@
      *                               {@link #MAX_DATA_BYTES}
      */
     @NonNull
-    @SuppressWarnings("AmbiguousMethodReference")
     public byte[] toByteArray() {
-        return Data.toByteArray(this);
+        return Data.toByteArrayInternal(this);
     }
 
      /**
@@ -384,7 +383,7 @@
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @TypeConverter
-    public static @NonNull byte[] toByteArray(@NonNull Data data) {
+    public static @NonNull byte[] toByteArrayInternal(@NonNull Data data) {
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         ObjectOutputStream objectOutputStream = null;
         try {
@@ -843,7 +842,7 @@
             Data data = new Data(mValues);
             // Make sure we catch Data objects that are too large at build() instead of later.  This
             // method will throw an exception if data is too big.
-            Data.toByteArray(data);
+            Data.toByteArrayInternal(data);
             return data;
         }
     }
diff --git a/work/workmanager/src/main/java/androidx/work/ExistingWorkPolicy.java b/work/workmanager/src/main/java/androidx/work/ExistingWorkPolicy.java
index eb2a507..5161c30 100644
--- a/work/workmanager/src/main/java/androidx/work/ExistingWorkPolicy.java
+++ b/work/workmanager/src/main/java/androidx/work/ExistingWorkPolicy.java
@@ -39,6 +39,20 @@
      * If there is existing pending (uncompleted) work with the same unique name, append the
      * newly-specified work as a child of all the leaves of that work sequence.  Otherwise, insert
      * the newly-specified work as the start of a new sequence.
+     * <br/>
+     * <b>Note:</b> When using APPEND with failed or cancelled prerequisites, newly enqueued work
+     * will also be marked as failed or cancelled respectively. Use
+     * {@link ExistingWorkPolicy#APPEND_OR_REPLACE} to create a new chain of work.
      */
-    APPEND
+    APPEND,
+
+    /**
+     * If there is existing pending (uncompleted) work with the same unique name, append the
+     * newly-specified work as the child of all the leaves of that work sequence. Otherwise, insert
+     * the newly-specified work as the start of a new sequence.
+     * <br/>
+     * <b>Note:</b> If there are failed or cancelled prerequisites, these prerequisites are
+     * <i>dropped</i> and the newly-specified work is the start of a new sequence.
+     */
+    APPEND_OR_REPLACE,
 }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/WorkDatabase.java b/work/workmanager/src/main/java/androidx/work/impl/WorkDatabase.java
index aa4c086..83a9ff9 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/WorkDatabase.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/WorkDatabase.java
@@ -36,6 +36,8 @@
 import androidx.room.RoomDatabase;
 import androidx.room.TypeConverters;
 import androidx.sqlite.db.SupportSQLiteDatabase;
+import androidx.sqlite.db.SupportSQLiteOpenHelper;
+import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory;
 import androidx.work.Data;
 import androidx.work.impl.model.Dependency;
 import androidx.work.impl.model.DependencyDao;
@@ -100,7 +102,7 @@
      */
     @NonNull
     public static WorkDatabase create(
-            @NonNull Context context,
+            @NonNull final Context context,
             @NonNull Executor queryExecutor,
             boolean useTestDatabase) {
         RoomDatabase.Builder<WorkDatabase> builder;
@@ -108,8 +110,23 @@
             builder = Room.inMemoryDatabaseBuilder(context, WorkDatabase.class)
                     .allowMainThreadQueries();
         } else {
-            String path = WorkDatabasePathHelper.getDatabasePath(context).getPath();
-            builder = Room.databaseBuilder(context, WorkDatabase.class, path);
+            String name = WorkDatabasePathHelper.getWorkDatabaseName();
+            builder = Room.databaseBuilder(context, WorkDatabase.class, name);
+            builder.openHelperFactory(new SupportSQLiteOpenHelper.Factory() {
+                @NonNull
+                @Override
+                public SupportSQLiteOpenHelper create(
+                        @NonNull SupportSQLiteOpenHelper.Configuration configuration) {
+                    SupportSQLiteOpenHelper.Configuration.Builder configBuilder =
+                            SupportSQLiteOpenHelper.Configuration.builder(context);
+                    configBuilder.name(configuration.name)
+                            .callback(configuration.callback)
+                            .noBackupDirectory(true);
+                    FrameworkSQLiteOpenHelperFactory factory =
+                            new FrameworkSQLiteOpenHelperFactory();
+                    return factory.create(configBuilder.build());
+                }
+            });
         }
 
         return builder.setQueryExecutor(queryExecutor)
diff --git a/work/workmanager/src/main/java/androidx/work/impl/WorkDatabasePathHelper.java b/work/workmanager/src/main/java/androidx/work/impl/WorkDatabasePathHelper.java
index aafcb32..0b4bf2b 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/WorkDatabasePathHelper.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/WorkDatabasePathHelper.java
@@ -47,26 +47,11 @@
     private static final String[] DATABASE_EXTRA_FILES = new String[]{"-journal", "-shm", "-wal"};
 
     /**
-     * @param context The application {@link Context}
-     * @return The database path before migration to the no-backup directory.
+     * @return The name of the database.
      */
     @NonNull
-    public static File getDefaultDatabasePath(@NonNull Context context) {
-        return context.getDatabasePath(WORK_DATABASE_NAME);
-    }
-
-    /**
-     * @param context The application {@link Context}
-     * @return The the migrated database path.
-     */
-    @NonNull
-    public static File getDatabasePath(@NonNull Context context) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
-            // No notion of a backup directory exists.
-            return getDefaultDatabasePath(context);
-        } else {
-            return getNoBackupPath(context, WORK_DATABASE_NAME);
-        }
+    public static String getWorkDatabaseName() {
+        return WORK_DATABASE_NAME;
     }
 
     /**
@@ -123,6 +108,31 @@
     }
 
     /**
+     * @param context The application {@link Context}
+     * @return The database path before migration to the no-backup directory.
+     */
+    @NonNull
+    @VisibleForTesting
+    public static File getDefaultDatabasePath(@NonNull Context context) {
+        return context.getDatabasePath(WORK_DATABASE_NAME);
+    }
+
+    /**
+     * @param context The application {@link Context}
+     * @return The the migrated database path.
+     */
+    @NonNull
+    @VisibleForTesting
+    public static File getDatabasePath(@NonNull Context context) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            // No notion of a backup directory exists.
+            return getDefaultDatabasePath(context);
+        } else {
+            return getNoBackupPath(context, WORK_DATABASE_NAME);
+        }
+    }
+
+    /**
      * Return the path for a {@link File} path in the {@link Context#getNoBackupFilesDir()}
      * identified by the {@link String} fragment.
      *
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java b/work/workmanager/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
index 82c885b..31a703f 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/EnqueueRunnable.java
@@ -17,6 +17,7 @@
 package androidx.work.impl.utils;
 
 import static androidx.work.ExistingWorkPolicy.APPEND;
+import static androidx.work.ExistingWorkPolicy.APPEND_OR_REPLACE;
 import static androidx.work.ExistingWorkPolicy.KEEP;
 import static androidx.work.WorkInfo.State.BLOCKED;
 import static androidx.work.WorkInfo.State.CANCELLED;
@@ -56,6 +57,7 @@
 import androidx.work.impl.workers.ConstraintTrackingWorker;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -224,7 +226,7 @@
 
             if (!existingWorkSpecIdAndStates.isEmpty()) {
                 // If appending, these are the new prerequisites.
-                if (existingWorkPolicy == APPEND) {
+                if (existingWorkPolicy == APPEND || existingWorkPolicy == APPEND_OR_REPLACE) {
                     DependencyDao dependencyDao = workDatabase.dependencyDao();
                     List<String> newPrerequisiteIds = new ArrayList<>();
                     for (WorkSpec.IdAndState idAndState : existingWorkSpecIdAndStates) {
@@ -238,6 +240,21 @@
                             newPrerequisiteIds.add(idAndState.id);
                         }
                     }
+                    if (existingWorkPolicy == APPEND_OR_REPLACE) {
+                        if (hasCancelledPrerequisites || hasFailedPrerequisites) {
+                            // Delete all WorkSpecs with this name
+                            WorkSpecDao workSpecDao = workDatabase.workSpecDao();
+                            List<WorkSpec.IdAndState> idAndStates =
+                                    workSpecDao.getWorkSpecIdAndStatesForName(name);
+                            for (WorkSpec.IdAndState idAndState : idAndStates) {
+                                workSpecDao.delete(idAndState.id);
+                            }
+                            // Treat this as a new chain of work.
+                            newPrerequisiteIds = Collections.emptyList();
+                            hasCancelledPrerequisites = false;
+                            hasFailedPrerequisites = false;
+                        }
+                    }
                     prerequisiteIds = newPrerequisiteIds.toArray(prerequisiteIds);
                     hasPrerequisite = (prerequisiteIds.length > 0);
                 } else {
diff --git a/work/workmanager/src/test/java/androidx/work/DataTest.java b/work/workmanager/src/test/java/androidx/work/DataTest.java
index 3019697..e31ffbc 100644
--- a/work/workmanager/src/test/java/androidx/work/DataTest.java
+++ b/work/workmanager/src/test/java/androidx/work/DataTest.java
@@ -80,7 +80,7 @@
                 .putIntArray(KEY2, expectedValue2)
                 .build();
 
-        byte[] byteArray = Data.toByteArray(data);
+        byte[] byteArray = Data.toByteArrayInternal(data);
         Data restoredData = Data.fromByteArray(byteArray);
 
         assertThat(restoredData, is(notNullValue()));