Merge "Properly log see more event for row view"
diff --git a/annotations/build.gradle b/annotations/build.gradle
index 03d2e6c..15274fe 100644
--- a/annotations/build.gradle
+++ b/annotations/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportJavaLibraryPlugin")
diff --git a/app-toolkit/common/build.gradle b/app-toolkit/common/build.gradle
index db61b3f..81a34ad 100644
--- a/app-toolkit/common/build.gradle
+++ b/app-toolkit/common/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension;
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension;
 
 plugins {
     id("SupportJavaLibraryPlugin")
diff --git a/app-toolkit/core-testing/build.gradle b/app-toolkit/core-testing/build.gradle
index 8e7ccfb..e4c2e17 100644
--- a/app-toolkit/core-testing/build.gradle
+++ b/app-toolkit/core-testing/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/app-toolkit/init.gradle b/app-toolkit/init.gradle
index d3d7754..3c58368 100644
--- a/app-toolkit/init.gradle
+++ b/app-toolkit/init.gradle
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-import android.support.DacOptions
-import android.support.license.CheckExternalDependencyLicensesTask
+import androidx.build.DacOptions
+import androidx.build.license.CheckExternalDependencyLicensesTask
+
 apply from: "${ext.supportRootFolder}/buildSrc/init.gradle"
 init.setSdkInLocalPropertiesFile()
 
diff --git a/app-toolkit/runtime/build.gradle b/app-toolkit/runtime/build.gradle
index cd74aae..5c8f2d9 100644
--- a/app-toolkit/runtime/build.gradle
+++ b/app-toolkit/runtime/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.SupportLibraryExtension
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.SupportLibraryExtension
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/asynclayoutinflater/build.gradle b/asynclayoutinflater/build.gradle
index 597b01d..a83d876 100644
--- a/asynclayoutinflater/build.gradle
+++ b/asynclayoutinflater/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/build.gradle b/build.gradle
index 0307507..c8683d4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import android.support.DacOptions
+
+import androidx.build.DacOptions
 
 def currentJvmVersion = org.gradle.api.JavaVersion.current()
 if (currentJvmVersion.getMajorVersion() != "8") {
diff --git a/buildSrc/init.gradle b/buildSrc/init.gradle
index 38b41e8..4faeac5 100644
--- a/buildSrc/init.gradle
+++ b/buildSrc/init.gradle
@@ -15,9 +15,9 @@
  */
 
 
-import android.support.DiffAndDocs
-import android.support.gmaven.GMavenVersionChecker
-import android.support.license.CheckExternalDependencyLicensesTask
+import androidx.build.DiffAndDocs
+import androidx.build.gmaven.GMavenVersionChecker
+import androidx.build.license.CheckExternalDependencyLicensesTask
 import com.android.build.gradle.internal.coverage.JacocoReportTask
 import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask
 import org.gradle.api.logging.configuration.ShowStacktrace
diff --git a/buildSrc/src/main/kotlin/android/support/DiffAndDocs.kt b/buildSrc/src/main/kotlin/androidx/build/DiffAndDocs.kt
similarity index 93%
rename from buildSrc/src/main/kotlin/android/support/DiffAndDocs.kt
rename to buildSrc/src/main/kotlin/androidx/build/DiffAndDocs.kt
index 31e2026..213a8a8 100644
--- a/buildSrc/src/main/kotlin/android/support/DiffAndDocs.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/DiffAndDocs.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
-import android.support.checkapi.ApiXmlConversionTask
-import android.support.checkapi.CheckApiTask
-import android.support.checkapi.UpdateApiTask
-import android.support.doclava.DoclavaTask
-import android.support.docs.GenerateDocsTask
-import android.support.jdiff.JDiffTask
+import androidx.build.checkapi.ApiXmlConversionTask
+import androidx.build.checkapi.CheckApiTask
+import androidx.build.checkapi.UpdateApiTask
+import androidx.build.doclava.DoclavaTask
+import androidx.build.docs.GenerateDocsTask
+import androidx.build.jdiff.JDiffTask
 import com.android.build.gradle.LibraryExtension
 import com.android.build.gradle.api.LibraryVariant
 import org.gradle.api.GradleException
@@ -137,28 +137,6 @@
     return File(apiDir, "current.txt")
 }
 
-private fun createVerifyUpdateApiAllowedTask(project: Project) =
-        project.tasks.createWithConfig("verifyUpdateApiAllowed") {
-            // This could be moved to doFirst inside updateApi, but using it as a
-            // dependency with no inputs forces it to run even when updateApi is a
-            // no-op.
-            doLast {
-                val rootFolder = project.projectDir
-                val version = Version(project.version as String)
-
-                if (version.isPatch()) {
-                    throw GradleException("Public APIs may not be modified in patch releases.")
-                } else if (!version.isFinalApi() && getApiFile(rootFolder,
-                        version,
-                        true).exists()) {
-                    throw GradleException("Inconsistent version. Public API file already exists.")
-                } else if (version.isFinalApi() && getApiFile(rootFolder, version).exists()
-                        && !project.hasProperty("force")) {
-                    throw GradleException("Public APIs may not be modified in finalized releases.")
-                }
-            }
-        }
-
 // Generates API files
 private fun createGenerateApiTask(project: Project, docletpathParam: Collection<File>) =
         project.tasks.createWithConfig("generateApi", DoclavaTask::class.java) {
@@ -232,8 +210,12 @@
             oldApiFile = getApiFile(project.projectDir, project.version())
             whitelistErrors = checkApiRelease.whitelistErrors
             whitelistErrorsFile = checkApiRelease.whitelistErrorsFile
-
             doFirst {
+                val version = project.version()
+                if (!version.isFinalApi() &&
+                        getApiFile(project.projectDir, version, true).exists()) {
+                    throw GradleException("Inconsistent version. Public API file already exists.")
+                }
                 // Replace the expected whitelist with the detected whitelist.
                 whitelistErrors = checkApiRelease.detectedWhitelistErrors
             }
@@ -246,7 +228,9 @@
  */
 private fun createOldApiXml(project: Project, doclavaConfig: Configuration) =
         project.tasks.createWithConfig("oldApiXml", ApiXmlConversionTask::class.java) {
-            val toApi = project.processProperty("toApi")?.let { Version.parseOrNull(it) }
+            val toApi = project.processProperty("toApi")?.let {
+                Version.parseOrNull(it)
+            }
             val fromApi = project.processProperty("fromApi")
             classpath = project.files(doclavaConfig.resolve())
             val rootFolder = project.projectDir
@@ -450,7 +434,6 @@
     val docletClasspath = doclavaConfiguration.resolve()
     val generateApi = createGenerateApiTask(project, docletClasspath)
     generateApi.dependsOn(doclavaConfiguration)
-    val verifyUpdateTask = createVerifyUpdateApiAllowedTask(project)
 
     // Make sure the API surface has not broken since the last release.
     val lastReleasedApiFile = getLastReleasedApiFile(workingDir, version)
@@ -489,7 +472,7 @@
     checkApi.description = "Verify the API surface."
 
     val updateApiTask = createUpdateApiTask(project, checkApiRelease)
-    updateApiTask.dependsOn(checkApiRelease, verifyUpdateTask)
+    updateApiTask.dependsOn(checkApiRelease)
     val newApiTask = createNewApiXmlTask(project, generateApi, doclavaConfiguration)
     val oldApiTask = createOldApiXml(project, doclavaConfiguration)
 
@@ -578,9 +561,6 @@
 private fun sdkApiFile(project: Project) = File(project.docsDir(), "release/sdk_current.txt")
 private fun removedSdkApiFile(project: Project) = File(project.docsDir(), "release/sdk_removed.txt")
 
-private fun TaskContainer.createWithConfig(name: String, config: Task.() -> Unit) =
-        create(name) { task -> task.config() }
-
 private fun <T : Task> TaskContainer.createWithConfig(
         name: String, taskClass: Class<T>,
         config: T.() -> Unit) =
diff --git a/buildSrc/src/main/kotlin/android/support/ErrorProneConfiguration.kt b/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
similarity index 98%
rename from buildSrc/src/main/kotlin/android/support/ErrorProneConfiguration.kt
rename to buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
index 74f0505..8c8fbbb 100644
--- a/buildSrc/src/main/kotlin/android/support/ErrorProneConfiguration.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
 import net.ltgt.gradle.errorprone.ErrorProneToolChain
 import org.gradle.api.tasks.compile.JavaCompile
diff --git a/buildSrc/src/main/kotlin/android/support/LibraryGroups.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
similarity index 97%
rename from buildSrc/src/main/kotlin/android/support/LibraryGroups.kt
rename to buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
index 2f64e5d..385c04c 100644
--- a/buildSrc/src/main/kotlin/android/support/LibraryGroups.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
 /**
  * The list of maven group names of all the libraries in this project.
diff --git a/buildSrc/src/main/kotlin/android/support/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
similarity index 96%
rename from buildSrc/src/main/kotlin/android/support/LibraryVersions.kt
rename to buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index cf5c647..fb4ab98 100644
--- a/buildSrc/src/main/kotlin/android/support/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
 /**
  * The list of versions codes of all the libraries in this project.
diff --git a/buildSrc/src/main/kotlin/android/support/MavenUploadHelper.kt b/buildSrc/src/main/kotlin/androidx/build/MavenUploadHelper.kt
similarity index 98%
rename from buildSrc/src/main/kotlin/android/support/MavenUploadHelper.kt
rename to buildSrc/src/main/kotlin/androidx/build/MavenUploadHelper.kt
index c8be47a..dc86936 100644
--- a/buildSrc/src/main/kotlin/android/support/MavenUploadHelper.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/MavenUploadHelper.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
 import com.android.build.gradle.LibraryPlugin
 import org.gradle.api.Project
diff --git a/buildSrc/src/main/kotlin/android/support/SourceJarTaskHelper.kt b/buildSrc/src/main/kotlin/androidx/build/SourceJarTaskHelper.kt
similarity index 98%
rename from buildSrc/src/main/kotlin/android/support/SourceJarTaskHelper.kt
rename to buildSrc/src/main/kotlin/androidx/build/SourceJarTaskHelper.kt
index 992171e..5a11175 100644
--- a/buildSrc/src/main/kotlin/android/support/SourceJarTaskHelper.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SourceJarTaskHelper.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
 import com.android.build.gradle.LibraryExtension
 import com.android.builder.core.BuilderConstants
diff --git a/buildSrc/src/main/kotlin/android/support/SupportAndroidLibraryPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/SupportAndroidLibraryPlugin.kt
similarity index 96%
rename from buildSrc/src/main/kotlin/android/support/SupportAndroidLibraryPlugin.kt
rename to buildSrc/src/main/kotlin/androidx/build/SupportAndroidLibraryPlugin.kt
index 14e4669..9c2d82c 100644
--- a/buildSrc/src/main/kotlin/android/support/SupportAndroidLibraryPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SupportAndroidLibraryPlugin.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
-import android.support.SupportConfig.INSTRUMENTATION_RUNNER
-import android.support.license.CheckExternalDependencyLicensesTask
+import androidx.build.SupportConfig.INSTRUMENTATION_RUNNER
+import androidx.build.license.CheckExternalDependencyLicensesTask
 import com.android.build.gradle.LibraryExtension
 import com.android.build.gradle.internal.dsl.LintOptions
 import com.android.build.gradle.tasks.GenerateBuildConfig
diff --git a/buildSrc/src/main/kotlin/android/support/SupportAndroidTestAppExtension.kt b/buildSrc/src/main/kotlin/androidx/build/SupportAndroidTestAppExtension.kt
similarity index 89%
rename from buildSrc/src/main/kotlin/android/support/SupportAndroidTestAppExtension.kt
rename to buildSrc/src/main/kotlin/androidx/build/SupportAndroidTestAppExtension.kt
index 66dd873..3d3e0aa 100644
--- a/buildSrc/src/main/kotlin/android/support/SupportAndroidTestAppExtension.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SupportAndroidTestAppExtension.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
-import android.support.SupportConfig.DEFAULT_MIN_SDK_VERSION
+import androidx.build.SupportConfig.DEFAULT_MIN_SDK_VERSION
 import org.gradle.api.Project
 
 /**
diff --git a/buildSrc/src/main/kotlin/android/support/SupportAndroidTestAppPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/SupportAndroidTestAppPlugin.kt
similarity index 94%
rename from buildSrc/src/main/kotlin/android/support/SupportAndroidTestAppPlugin.kt
rename to buildSrc/src/main/kotlin/androidx/build/SupportAndroidTestAppPlugin.kt
index 1604bbe..1142ea0 100644
--- a/buildSrc/src/main/kotlin/android/support/SupportAndroidTestAppPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SupportAndroidTestAppPlugin.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
-import android.support.SupportConfig.INSTRUMENTATION_RUNNER
-import android.support.license.CheckExternalDependencyLicensesTask
+import androidx.build.SupportConfig.INSTRUMENTATION_RUNNER
+import androidx.build.license.CheckExternalDependencyLicensesTask
 import com.android.build.gradle.AppExtension
 import net.ltgt.gradle.errorprone.ErrorProneBasePlugin
 import net.ltgt.gradle.errorprone.ErrorProneToolChain
diff --git a/buildSrc/src/main/kotlin/android/support/SupportConfig.kt b/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt
similarity index 97%
rename from buildSrc/src/main/kotlin/android/support/SupportConfig.kt
rename to buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt
index 42b298c..1f216f7 100644
--- a/buildSrc/src/main/kotlin/android/support/SupportConfig.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
 import org.gradle.api.Project
 import org.gradle.api.plugins.ExtraPropertiesExtension
diff --git a/buildSrc/src/main/kotlin/android/support/SupportJavaLibraryPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/SupportJavaLibraryPlugin.kt
similarity index 93%
rename from buildSrc/src/main/kotlin/android/support/SupportJavaLibraryPlugin.kt
rename to buildSrc/src/main/kotlin/androidx/build/SupportJavaLibraryPlugin.kt
index f3651ab..24546f1 100644
--- a/buildSrc/src/main/kotlin/android/support/SupportJavaLibraryPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SupportJavaLibraryPlugin.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
-import android.support.license.CheckExternalDependencyLicensesTask
+import androidx.build.license.CheckExternalDependencyLicensesTask
 import net.ltgt.gradle.errorprone.ErrorProneBasePlugin
 import net.ltgt.gradle.errorprone.ErrorProneToolChain
 import org.gradle.api.JavaVersion
diff --git a/buildSrc/src/main/kotlin/android/support/SupportKotlinLibraryPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/SupportKotlinLibraryPlugin.kt
similarity index 90%
rename from buildSrc/src/main/kotlin/android/support/SupportKotlinLibraryPlugin.kt
rename to buildSrc/src/main/kotlin/androidx/build/SupportKotlinLibraryPlugin.kt
index 8651ef8..4103aad 100644
--- a/buildSrc/src/main/kotlin/android/support/SupportKotlinLibraryPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SupportKotlinLibraryPlugin.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,9 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support
+package androidx.build
 
-import android.support.license.CheckExternalDependencyLicensesTask
+import androidx.build.license.CheckExternalDependencyLicensesTask
 import org.gradle.api.JavaVersion
 import org.gradle.api.Plugin
 import org.gradle.api.Project
diff --git a/buildSrc/src/main/kotlin/android/support/SupportLibraryExtension.kt b/buildSrc/src/main/kotlin/androidx/build/SupportLibraryExtension.kt
similarity index 93%
rename from buildSrc/src/main/kotlin/android/support/SupportLibraryExtension.kt
rename to buildSrc/src/main/kotlin/androidx/build/SupportLibraryExtension.kt
index fb2f3b5..4f69460 100644
--- a/buildSrc/src/main/kotlin/android/support/SupportLibraryExtension.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SupportLibraryExtension.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
-import android.support.SupportConfig.DEFAULT_MIN_SDK_VERSION
+import androidx.build.SupportConfig.DEFAULT_MIN_SDK_VERSION
 import groovy.lang.Closure
 import org.gradle.api.Project
 import java.util.ArrayList
diff --git a/buildSrc/src/main/kotlin/android/support/Version.kt b/buildSrc/src/main/kotlin/androidx/build/Version.kt
similarity index 98%
rename from buildSrc/src/main/kotlin/android/support/Version.kt
rename to buildSrc/src/main/kotlin/androidx/build/Version.kt
index 6348b0c..75855d5 100644
--- a/buildSrc/src/main/kotlin/android/support/Version.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/Version.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
 import java.io.File
 import java.util.regex.Matcher
diff --git a/buildSrc/src/main/kotlin/android/support/VersionFileWriterTask.kt b/buildSrc/src/main/kotlin/androidx/build/VersionFileWriterTask.kt
similarity index 98%
rename from buildSrc/src/main/kotlin/android/support/VersionFileWriterTask.kt
rename to buildSrc/src/main/kotlin/androidx/build/VersionFileWriterTask.kt
index cde11e6..9fbe3ba 100644
--- a/buildSrc/src/main/kotlin/android/support/VersionFileWriterTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/VersionFileWriterTask.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
 import com.android.build.gradle.LibraryExtension
 import org.gradle.api.DefaultTask
diff --git a/buildSrc/src/main/kotlin/android/support/checkapi/ApiXmlConversionTask.kt b/buildSrc/src/main/kotlin/androidx/build/checkapi/ApiXmlConversionTask.kt
similarity index 97%
rename from buildSrc/src/main/kotlin/android/support/checkapi/ApiXmlConversionTask.kt
rename to buildSrc/src/main/kotlin/androidx/build/checkapi/ApiXmlConversionTask.kt
index a4733b2..419d66f 100644
--- a/buildSrc/src/main/kotlin/android/support/checkapi/ApiXmlConversionTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/checkapi/ApiXmlConversionTask.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.checkapi
+package androidx.build.checkapi
 
 import org.gradle.api.tasks.InputFile
 import org.gradle.api.tasks.JavaExec
diff --git a/buildSrc/src/main/kotlin/android/support/checkapi/CheckApiTask.kt b/buildSrc/src/main/kotlin/androidx/build/checkapi/CheckApiTask.kt
similarity index 99%
rename from buildSrc/src/main/kotlin/android/support/checkapi/CheckApiTask.kt
rename to buildSrc/src/main/kotlin/androidx/build/checkapi/CheckApiTask.kt
index be9f877..44f7d39 100644
--- a/buildSrc/src/main/kotlin/android/support/checkapi/CheckApiTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/checkapi/CheckApiTask.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.checkapi
+package androidx.build.checkapi
 
 import org.gradle.api.DefaultTask
 import org.gradle.api.GradleException
diff --git a/buildSrc/src/main/kotlin/android/support/checkapi/UpdateApiTask.kt b/buildSrc/src/main/kotlin/androidx/build/checkapi/UpdateApiTask.kt
similarity index 79%
rename from buildSrc/src/main/kotlin/android/support/checkapi/UpdateApiTask.kt
rename to buildSrc/src/main/kotlin/androidx/build/checkapi/UpdateApiTask.kt
index e1c0d3f..810d50b 100644
--- a/buildSrc/src/main/kotlin/android/support/checkapi/UpdateApiTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/checkapi/UpdateApiTask.kt
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
-package android.support.checkapi
+package androidx.build.checkapi
 
+import androidx.build.Version
 import com.google.common.io.Files
 import org.gradle.api.DefaultTask
+import org.gradle.api.GradleException
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFile
 import org.gradle.api.tasks.Optional
@@ -52,6 +54,18 @@
      */
     @TaskAction
     fun doUpdate() {
+        if (oldApiFile.exists() && newApiFile.readText() == oldApiFile.readText()) {
+            // whatever nothing changed
+            return
+        }
+
+        val version = Version(project.version as String)
+        if (version.isPatch()) {
+            throw GradleException("Public APIs may not be modified in patch releases.")
+        } else if (version.isFinalApi() && oldApiFile.exists() && !project.hasProperty("force")) {
+            throw GradleException("Public APIs may not be modified in finalized releases.")
+        }
+
         Files.copy(newApiFile, oldApiFile)
 
         if (oldRemovedApiFile != null) {
diff --git a/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
similarity index 98%
rename from buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt
rename to buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 0844832..4199fae 100644
--- a/buildSrc/src/main/kotlin/android/support/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.dependencies
+package androidx.build.dependencies
 
 const val AUTO_COMMON = "com.google.auto:auto-common:0.6"
 const val ANTLR = "org.antlr:antlr4:4.5.3"
diff --git a/buildSrc/src/main/kotlin/android/support/doclava/DoclavaTask.kt b/buildSrc/src/main/kotlin/androidx/build/doclava/DoclavaTask.kt
similarity index 99%
rename from buildSrc/src/main/kotlin/android/support/doclava/DoclavaTask.kt
rename to buildSrc/src/main/kotlin/androidx/build/doclava/DoclavaTask.kt
index b30611d..3f0a6f5 100644
--- a/buildSrc/src/main/kotlin/android/support/doclava/DoclavaTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/doclava/DoclavaTask.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.doclava
+package androidx.build.doclava
 
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFiles
diff --git a/buildSrc/src/main/kotlin/android/support/docs/GenerateDocsTask.kt b/buildSrc/src/main/kotlin/androidx/build/docs/GenerateDocsTask.kt
similarity index 94%
rename from buildSrc/src/main/kotlin/android/support/docs/GenerateDocsTask.kt
rename to buildSrc/src/main/kotlin/androidx/build/docs/GenerateDocsTask.kt
index e889a54..4bbb830 100644
--- a/buildSrc/src/main/kotlin/android/support/docs/GenerateDocsTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/docs/GenerateDocsTask.kt
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package android.support.docs
+package androidx.build.docs
 
-import android.support.Version
-import android.support.doclava.DoclavaTask
+import androidx.build.Version
+import androidx.build.doclava.DoclavaTask
 import java.io.File
 
 open class GenerateDocsTask : DoclavaTask() {
diff --git a/buildSrc/src/main/kotlin/android/support/gmaven/GMavenVersionChecker.kt b/buildSrc/src/main/kotlin/androidx/build/gmaven/GMavenVersionChecker.kt
similarity index 98%
rename from buildSrc/src/main/kotlin/android/support/gmaven/GMavenVersionChecker.kt
rename to buildSrc/src/main/kotlin/androidx/build/gmaven/GMavenVersionChecker.kt
index 7fc4836..3c1b8d0 100644
--- a/buildSrc/src/main/kotlin/android/support/gmaven/GMavenVersionChecker.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/gmaven/GMavenVersionChecker.kt
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.support.gmaven
+package androidx.build.gmaven
 
-import android.support.Version
+import androidx.build.Version
 import groovy.util.XmlSlurper
 import groovy.util.slurpersupport.Node
 import groovy.util.slurpersupport.NodeChild
diff --git a/buildSrc/src/main/kotlin/android/support/jdiff/JDiffTask.kt b/buildSrc/src/main/kotlin/androidx/build/jdiff/JDiffTask.kt
similarity index 98%
rename from buildSrc/src/main/kotlin/android/support/jdiff/JDiffTask.kt
rename to buildSrc/src/main/kotlin/androidx/build/jdiff/JDiffTask.kt
index a6abc57..dfeb322 100644
--- a/buildSrc/src/main/kotlin/android/support/jdiff/JDiffTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/jdiff/JDiffTask.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.jdiff
+package androidx.build.jdiff
 
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputFile
diff --git a/buildSrc/src/main/kotlin/android/support/license/CheckExternalDependencyLicensesTask.kt b/buildSrc/src/main/kotlin/androidx/build/license/CheckExternalDependencyLicensesTask.kt
similarity index 98%
rename from buildSrc/src/main/kotlin/android/support/license/CheckExternalDependencyLicensesTask.kt
rename to buildSrc/src/main/kotlin/androidx/build/license/CheckExternalDependencyLicensesTask.kt
index 771f222..2dc4e01 100644
--- a/buildSrc/src/main/kotlin/android/support/license/CheckExternalDependencyLicensesTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/license/CheckExternalDependencyLicensesTask.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support.license
+package androidx.build.license
 
 import org.gradle.api.DefaultTask
 import org.gradle.api.GradleException
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportAndroidLibraryPlugin.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportAndroidLibraryPlugin.properties
index 222602b..25c8e4e 100644
--- a/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportAndroidLibraryPlugin.properties
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportAndroidLibraryPlugin.properties
@@ -1 +1 @@
-implementation-class=android.support.SupportAndroidLibraryPlugin
\ No newline at end of file
+implementation-class=androidx.build.SupportAndroidLibraryPlugin
\ No newline at end of file
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportAndroidTestAppPlugin.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportAndroidTestAppPlugin.properties
index 4c794f1..8bb9716 100644
--- a/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportAndroidTestAppPlugin.properties
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportAndroidTestAppPlugin.properties
@@ -1 +1 @@
-implementation-class=android.support.SupportAndroidTestAppPlugin
\ No newline at end of file
+implementation-class=androidx.build.SupportAndroidTestAppPlugin
\ No newline at end of file
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportJavaLibraryPlugin.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportJavaLibraryPlugin.properties
index 89755b7..acc6819 100644
--- a/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportJavaLibraryPlugin.properties
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportJavaLibraryPlugin.properties
@@ -1 +1 @@
-implementation-class=android.support.SupportJavaLibraryPlugin
\ No newline at end of file
+implementation-class=androidx.build.SupportJavaLibraryPlugin
\ No newline at end of file
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportKotlinLibraryPlugin.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportKotlinLibraryPlugin.properties
index d93c20a..f50d743 100644
--- a/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportKotlinLibraryPlugin.properties
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/SupportKotlinLibraryPlugin.properties
@@ -1 +1 @@
-implementation-class=android.support.SupportKotlinLibraryPlugin
+implementation-class=androidx.build.SupportKotlinLibraryPlugin
diff --git a/buildSrc/src/test/kotlin/android/support/VersionTest.kt b/buildSrc/src/test/kotlin/androidx/build/VersionTest.kt
similarity index 95%
rename from buildSrc/src/test/kotlin/android/support/VersionTest.kt
rename to buildSrc/src/test/kotlin/androidx/build/VersionTest.kt
index 339acab..fc803d9 100644
--- a/buildSrc/src/test/kotlin/android/support/VersionTest.kt
+++ b/buildSrc/src/test/kotlin/androidx/build/VersionTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support
+package androidx.build
 
 import org.junit.Assert.assertEquals
 import org.junit.Test
diff --git a/car/build.gradle b/car/build.gradle
index 819f0ad..6c8eb03 100644
--- a/car/build.gradle
+++ b/car/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java b/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
index e7e8ec6..d489c50 100644
--- a/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
+++ b/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
@@ -730,6 +730,29 @@
     }
 
     @Test
+    public void testNoCarriedOverOnClickListener() throws Throwable {
+        boolean[] clicked = new boolean[] {false};
+        TextListItem item0 = new TextListItem(mActivity);
+        item0.setOnClickListener(v -> clicked[0] = true);
+
+        setupPagedListView(Arrays.asList(item0));
+
+        onView(withId(R.id.recycler_view)).perform(actionOnItemAtPosition(0, click()));
+        assertTrue(clicked[0]);
+
+        // item1 does not have onClickListener.
+        TextListItem item1 = new TextListItem(mActivity);
+        TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
+        // Manually rebind the view holder.
+        mActivityRule.runOnUiThread(() -> item1.bind(viewHolder));
+
+        // Reset for testing.
+        clicked[0] = false;
+        onView(withId(R.id.recycler_view)).perform(actionOnItemAtPosition(0, click()));
+        assertFalse(clicked[0]);
+    }
+
+    @Test
     public void testUpdateItem() throws Throwable {
         TextListItem item = new TextListItem(mActivity);
         setupPagedListView(Arrays.asList(item));
diff --git a/car/src/main/java/androidx/car/drawer/DrawerItemViewHolder.java b/car/src/main/java/androidx/car/drawer/DrawerItemViewHolder.java
index 8994444..a374f8d 100644
--- a/car/src/main/java/androidx/car/drawer/DrawerItemViewHolder.java
+++ b/car/src/main/java/androidx/car/drawer/DrawerItemViewHolder.java
@@ -95,7 +95,7 @@
      *
      * @param restrictions current car UX restrictions.
      */
-    public void complyWithUxRestrictions(CarUxRestrictions restrictions) {
+    void complyWithUxRestrictions(CarUxRestrictions restrictions) {
         CarUxRestrictionsUtils.comply(itemView.getContext(), restrictions, getText());
     }
 }
diff --git a/car/src/main/java/androidx/car/moderator/SpeedBumpController.java b/car/src/main/java/androidx/car/moderator/SpeedBumpController.java
index 21cf124..dcaedc1 100644
--- a/car/src/main/java/androidx/car/moderator/SpeedBumpController.java
+++ b/car/src/main/java/androidx/car/moderator/SpeedBumpController.java
@@ -245,8 +245,17 @@
             try {
                 mCarUxRestrictionsManager = (CarUxRestrictionsManager)
                         mCar.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+
+                // Use explicit class definition instead of lambda. Using lambda makes compiler to
+                // desugar, which will lead to failure due to class definition not being available
+                // to dependencies at compile time (e.g. sample apk).
                 mCarUxRestrictionsManager.registerListener(
-                        SpeedBumpController.this::updateUnlimitedModeEnabled);
+                        new CarUxRestrictionsManager.onUxRestrictionsChangedListener() {
+                            @Override
+                            public void onUxRestrictionsChanged(CarUxRestrictions uxRestrictions) {
+                                updateUnlimitedModeEnabled(uxRestrictions);
+                            }
+                        });
 
                 updateUnlimitedModeEnabled(
                         mCarUxRestrictionsManager.getCurrentCarUxRestrictions());
diff --git a/car/src/main/java/androidx/car/utils/CarUxRestrictionsUtils.java b/car/src/main/java/androidx/car/utils/CarUxRestrictionsUtils.java
index 6b4019c..73d4c98 100644
--- a/car/src/main/java/androidx/car/utils/CarUxRestrictionsUtils.java
+++ b/car/src/main/java/androidx/car/utils/CarUxRestrictionsUtils.java
@@ -16,8 +16,11 @@
 
 package androidx.car.utils;
 
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
 import android.car.drivingstate.CarUxRestrictions;
 import android.content.Context;
+import android.support.annotation.RestrictTo;
 import android.text.InputFilter;
 import android.widget.TextView;
 
@@ -29,7 +32,10 @@
 
 /**
  * Utility class that helps {@code View}s comply with {@link CarUxRestrictions}.
+ *
+ * @hide
  */
+@RestrictTo(LIBRARY_GROUP)
 public class CarUxRestrictionsUtils {
 
     private CarUxRestrictionsUtils() {};
diff --git a/car/src/main/java/androidx/car/widget/ListItem.java b/car/src/main/java/androidx/car/widget/ListItem.java
index 014ffd7..dd319e8 100644
--- a/car/src/main/java/androidx/car/widget/ListItem.java
+++ b/car/src/main/java/androidx/car/widget/ListItem.java
@@ -221,6 +221,6 @@
          *
          * @param restrictions current car UX restrictions.
          */
-        public abstract void complyWithUxRestrictions(CarUxRestrictions restrictions);
+        abstract void complyWithUxRestrictions(CarUxRestrictions restrictions);
     }
 }
diff --git a/car/src/main/java/androidx/car/widget/SeekbarListItem.java b/car/src/main/java/androidx/car/widget/SeekbarListItem.java
index 0136cd2..796fd24 100644
--- a/car/src/main/java/androidx/car/widget/SeekbarListItem.java
+++ b/car/src/main/java/androidx/car/widget/SeekbarListItem.java
@@ -520,7 +520,7 @@
         }
 
         @Override
-        public void complyWithUxRestrictions(CarUxRestrictions restrictions) {
+        void complyWithUxRestrictions(CarUxRestrictions restrictions) {
             CarUxRestrictionsUtils.comply(itemView.getContext(), restrictions, getText());
         }
 
diff --git a/car/src/main/java/androidx/car/widget/TextListItem.java b/car/src/main/java/androidx/car/widget/TextListItem.java
index df8d5fa..24c1b12 100644
--- a/car/src/main/java/androidx/car/widget/TextListItem.java
+++ b/car/src/main/java/androidx/car/widget/TextListItem.java
@@ -230,9 +230,7 @@
     }
 
     private void setOnClickListener() {
-        if (mOnClickListener != null) {
-            mBinders.add(vh -> vh.itemView.setOnClickListener(mOnClickListener));
-        }
+        mBinders.add(vh -> vh.itemView.setOnClickListener(mOnClickListener));
     }
 
     private void setPrimaryIconContent() {
@@ -764,7 +762,7 @@
          * @param restrictions current car UX restrictions.
          */
         @Override
-        public void complyWithUxRestrictions(CarUxRestrictions restrictions) {
+        void complyWithUxRestrictions(CarUxRestrictions restrictions) {
             CarUxRestrictionsUtils.comply(itemView.getContext(), restrictions, getBody());
         }
 
diff --git a/collections/build.gradle b/collections/build.gradle
index 3145c4a..a5ebc43 100644
--- a/collections/build.gradle
+++ b/collections/build.gradle
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportJavaLibraryPlugin")
diff --git a/compat/build.gradle b/compat/build.gradle
index f52c5ff..3cb9b06 100644
--- a/compat/build.gradle
+++ b/compat/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
@@ -15,7 +15,7 @@
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation project(':support-testutils'), {
+    androidTestImplementation project(':internal-testutils'), {
         exclude group: 'com.android.support', module: 'support-compat'
     }
 }
diff --git a/compat/src/androidTest/java/android/support/v4/widget/ContentLoadingProgressBarTest.java b/compat/src/androidTest/java/android/support/v4/widget/ContentLoadingProgressBarTest.java
index c8bc229..9375aad 100644
--- a/compat/src/androidTest/java/android/support/v4/widget/ContentLoadingProgressBarTest.java
+++ b/compat/src/androidTest/java/android/support/v4/widget/ContentLoadingProgressBarTest.java
@@ -22,7 +22,6 @@
 import android.support.test.filters.LargeTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.testutils.PollingCheck;
 import android.view.View;
 
 import org.junit.Before;
@@ -30,6 +29,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import androidx.testutils.PollingCheck;
+
 /**
  * Tests for {@link ContentLoadingProgressBar}
  */
diff --git a/content/build.gradle b/content/build.gradle
index 990f308..ea39651 100644
--- a/content/build.gradle
+++ b/content/build.gradle
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/coordinatorlayout/build.gradle b/coordinatorlayout/build.gradle
index c6ce55b..0f9e7ab 100644
--- a/coordinatorlayout/build.gradle
+++ b/coordinatorlayout/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
@@ -15,7 +15,7 @@
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(ESPRESSO_CONTRIB, libs.exclude_support)
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation project(':support-testutils'), {
+    androidTestImplementation project(':internal-testutils'), {
         exclude group: 'com.android.support', module: 'support-core-ui'
     }
 }
diff --git a/coordinatorlayout/src/androidTest/java/android/support/design/widget/BaseTestActivity.java b/coordinatorlayout/src/androidTest/java/android/support/design/widget/BaseTestActivity.java
index 0dd56e4..4272f56 100755
--- a/coordinatorlayout/src/androidTest/java/android/support/design/widget/BaseTestActivity.java
+++ b/coordinatorlayout/src/androidTest/java/android/support/design/widget/BaseTestActivity.java
@@ -18,9 +18,10 @@
 
 import android.os.Bundle;
 import android.support.annotation.LayoutRes;
-import android.support.testutils.RecreatedActivity;
 import android.view.WindowManager;
 
+import androidx.testutils.RecreatedActivity;
+
 abstract class BaseTestActivity extends RecreatedActivity {
 
     private boolean mDestroyed;
diff --git a/core-ui/build.gradle b/core-ui/build.gradle
index 12f5525..9fd62d1 100644
--- a/core-ui/build.gradle
+++ b/core-ui/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
@@ -24,7 +24,7 @@
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(ESPRESSO_CONTRIB, libs.exclude_support)
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation project(':support-testutils'), {
+    androidTestImplementation project(':internal-testutils'), {
         exclude group: 'com.android.support', module: 'support-core-ui'
     }
 
diff --git a/core-utils/build.gradle b/core-utils/build.gradle
index ffc0ab8..9f66444 100644
--- a/core-utils/build.gradle
+++ b/core-utils/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/cursoradapter/build.gradle b/cursoradapter/build.gradle
index d08a8bc..2e379a1 100644
--- a/cursoradapter/build.gradle
+++ b/cursoradapter/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/customtabs/build.gradle b/customtabs/build.gradle
index ced95b8..8df42d0 100644
--- a/customtabs/build.gradle
+++ b/customtabs/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
@@ -14,7 +14,7 @@
 
     androidTestImplementation(TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
-    androidTestImplementation(project(":support-testutils"))
+    androidTestImplementation(project(":internal-testutils"))
 }
 
 supportLibrary {
diff --git a/customtabs/src/androidTest/java/android/support/customtabs/PostMessageServiceConnectionTest.java b/customtabs/src/androidTest/java/android/support/customtabs/PostMessageServiceConnectionTest.java
index 07d21a8..381afae 100644
--- a/customtabs/src/androidTest/java/android/support/customtabs/PostMessageServiceConnectionTest.java
+++ b/customtabs/src/androidTest/java/android/support/customtabs/PostMessageServiceConnectionTest.java
@@ -26,7 +26,6 @@
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.rule.ServiceTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.testutils.PollingCheck;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -35,6 +34,8 @@
 
 import java.util.concurrent.TimeoutException;
 
+import androidx.testutils.PollingCheck;
+
 /**
  * Tests for {@link PostMessageServiceConnection} with no {@link CustomTabsService} component.
  */
diff --git a/customtabs/src/androidTest/java/android/support/customtabs/PostMessageTest.java b/customtabs/src/androidTest/java/android/support/customtabs/PostMessageTest.java
index 7e342b4..6e96b45 100644
--- a/customtabs/src/androidTest/java/android/support/customtabs/PostMessageTest.java
+++ b/customtabs/src/androidTest/java/android/support/customtabs/PostMessageTest.java
@@ -29,7 +29,6 @@
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.rule.ServiceTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.testutils.PollingCheck;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -38,6 +37,8 @@
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import androidx.testutils.PollingCheck;
+
 
 /**
  * Tests for a complete loop between a browser side {@link CustomTabsService}
diff --git a/customview/build.gradle b/customview/build.gradle
index 108ec71..421a2e0 100644
--- a/customview/build.gradle
+++ b/customview/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/documentfile/build.gradle b/documentfile/build.gradle
index 2f59518..460332f 100644
--- a/documentfile/build.gradle
+++ b/documentfile/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/drawerlayout/build.gradle b/drawerlayout/build.gradle
index 1d1476b..b4cc482 100644
--- a/drawerlayout/build.gradle
+++ b/drawerlayout/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/dynamic-animation/build.gradle b/dynamic-animation/build.gradle
index 451e874..04d3c33 100644
--- a/dynamic-animation/build.gradle
+++ b/dynamic-animation/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/emoji/appcompat/build.gradle b/emoji/appcompat/build.gradle
index 8a75f66..5c00c8e 100644
--- a/emoji/appcompat/build.gradle
+++ b/emoji/appcompat/build.gradle
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/emoji/bundled/build.gradle b/emoji/bundled/build.gradle
index 7c3a76a..181f893 100644
--- a/emoji/bundled/build.gradle
+++ b/emoji/bundled/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/emoji/core/build.gradle b/emoji/core/build.gradle
index 449ebb1..4936635 100644
--- a/emoji/core/build.gradle
+++ b/emoji/core/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 import java.util.zip.ZipException
 import java.util.zip.ZipFile
 
@@ -28,7 +28,7 @@
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation project(':support-testutils')
+    androidTestImplementation project(':internal-testutils')
 }
 
 android {
diff --git a/emoji/core/src/androidTest/java/android/support/text/emoji/EmojiKeyboardTest.java b/emoji/core/src/androidTest/java/android/support/text/emoji/EmojiKeyboardTest.java
index 1c647ce..aafa0d1 100644
--- a/emoji/core/src/androidTest/java/android/support/text/emoji/EmojiKeyboardTest.java
+++ b/emoji/core/src/androidTest/java/android/support/text/emoji/EmojiKeyboardTest.java
@@ -30,7 +30,6 @@
 import android.support.test.filters.Suppress;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.testutils.PollingCheck;
 import android.support.text.emoji.test.R;
 import android.support.text.emoji.util.KeyboardUtil;
 import android.support.text.emoji.util.TestString;
@@ -44,6 +43,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import androidx.testutils.PollingCheck;
+
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 @Suppress
diff --git a/exifinterface/build.gradle b/exifinterface/build.gradle
index e81d422..1914d9c 100644
--- a/exifinterface/build.gradle
+++ b/exifinterface/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/fragment/build.gradle b/fragment/build.gradle
index 4a3c1d1..9e7f478 100644
--- a/fragment/build.gradle
+++ b/fragment/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
@@ -18,7 +18,7 @@
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation project(':support-testutils'), {
+    androidTestImplementation project(':internal-testutils'), {
         exclude group: 'com.android.support', module: 'support-fragment'
     }
 }
diff --git a/fragment/src/androidTest/java/android/support/v4/app/FragmentManagerNonConfigTest.java b/fragment/src/androidTest/java/android/support/v4/app/FragmentManagerNonConfigTest.java
index dc62c01..c3bea05 100644
--- a/fragment/src/androidTest/java/android/support/v4/app/FragmentManagerNonConfigTest.java
+++ b/fragment/src/androidTest/java/android/support/v4/app/FragmentManagerNonConfigTest.java
@@ -21,13 +21,14 @@
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.testutils.FragmentActivityUtils;
 import android.support.v4.app.test.NonConfigOnStopActivity;
 
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import androidx.testutils.FragmentActivityUtils;
+
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class FragmentManagerNonConfigTest {
diff --git a/fragment/src/androidTest/java/android/support/v4/app/HangingFragmentTest.java b/fragment/src/androidTest/java/android/support/v4/app/HangingFragmentTest.java
index bf8726f..bb8d1db 100644
--- a/fragment/src/androidTest/java/android/support/v4/app/HangingFragmentTest.java
+++ b/fragment/src/androidTest/java/android/support/v4/app/HangingFragmentTest.java
@@ -19,7 +19,6 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.testutils.FragmentActivityUtils;
 import android.support.v4.app.test.HangingFragmentActivity;
 
 import org.junit.Assert;
@@ -27,6 +26,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import androidx.testutils.FragmentActivityUtils;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
diff --git a/fragment/src/androidTest/java/android/support/v4/app/LoaderTest.java b/fragment/src/androidTest/java/android/support/v4/app/LoaderTest.java
index 1d89e27..4a1837f 100644
--- a/fragment/src/androidTest/java/android/support/v4/app/LoaderTest.java
+++ b/fragment/src/androidTest/java/android/support/v4/app/LoaderTest.java
@@ -32,8 +32,6 @@
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.testutils.FragmentActivityUtils;
-import android.support.testutils.RecreatedActivity;
 import android.support.v4.app.test.LoaderActivity;
 import android.support.v4.content.AsyncTaskLoader;
 import android.support.v4.content.Loader;
@@ -46,6 +44,9 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import androidx.testutils.FragmentActivityUtils;
+import androidx.testutils.RecreatedActivity;
+
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class LoaderTest {
diff --git a/fragment/src/androidTest/java/android/support/v4/app/test/HangingFragmentActivity.java b/fragment/src/androidTest/java/android/support/v4/app/test/HangingFragmentActivity.java
index 80b9aa5..bdf29cb 100644
--- a/fragment/src/androidTest/java/android/support/v4/app/test/HangingFragmentActivity.java
+++ b/fragment/src/androidTest/java/android/support/v4/app/test/HangingFragmentActivity.java
@@ -19,7 +19,8 @@
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.fragment.test.R;
-import android.support.testutils.RecreatedActivity;
+
+import androidx.testutils.RecreatedActivity;
 
 public class HangingFragmentActivity extends RecreatedActivity {
 
diff --git a/fragment/src/androidTest/java/android/support/v4/app/test/LoaderActivity.java b/fragment/src/androidTest/java/android/support/v4/app/test/LoaderActivity.java
index f6ddf9c..1c88dbe 100644
--- a/fragment/src/androidTest/java/android/support/v4/app/test/LoaderActivity.java
+++ b/fragment/src/androidTest/java/android/support/v4/app/test/LoaderActivity.java
@@ -21,7 +21,6 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.fragment.test.R;
-import android.support.testutils.RecreatedActivity;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.AsyncTaskLoader;
@@ -31,6 +30,8 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import androidx.testutils.RecreatedActivity;
+
 public class LoaderActivity extends RecreatedActivity
         implements LoaderManager.LoaderCallbacks<String> {
     private static final int TEXT_LOADER_ID = 14;
diff --git a/fragment/src/androidTest/java/android/support/v4/app/test/NonConfigOnStopActivity.java b/fragment/src/androidTest/java/android/support/v4/app/test/NonConfigOnStopActivity.java
index 9d71388..f52ddbd 100644
--- a/fragment/src/androidTest/java/android/support/v4/app/test/NonConfigOnStopActivity.java
+++ b/fragment/src/androidTest/java/android/support/v4/app/test/NonConfigOnStopActivity.java
@@ -16,9 +16,10 @@
 
 package android.support.v4.app.test;
 
-import android.support.testutils.RecreatedActivity;
 import android.support.v4.app.Fragment;
 
+import androidx.testutils.RecreatedActivity;
+
 public class NonConfigOnStopActivity extends RecreatedActivity {
     @Override
     protected void onStop() {
diff --git a/fragment/src/main/java/android/support/v4/app/DialogFragment.java b/fragment/src/main/java/android/support/v4/app/DialogFragment.java
index b6bbb48..b585863 100644
--- a/fragment/src/main/java/android/support/v4/app/DialogFragment.java
+++ b/fragment/src/main/java/android/support/v4/app/DialogFragment.java
@@ -320,7 +320,8 @@
     }
 
     @Override
-    public LayoutInflater onGetLayoutInflater(Bundle savedInstanceState) {
+    @NonNull
+    public LayoutInflater onGetLayoutInflater(@Nullable Bundle savedInstanceState) {
         if (!mShowsDialog) {
             return super.onGetLayoutInflater(savedInstanceState);
         }
@@ -375,7 +376,7 @@
      * @return Return a new Dialog instance to be displayed by the Fragment.
      */
     @NonNull
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
+    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
         return new Dialog(getActivity(), getTheme());
     }
 
@@ -395,7 +396,7 @@
     }
 
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
+    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
         if (!mShowsDialog) {
@@ -436,7 +437,7 @@
     }
 
     @Override
-    public void onSaveInstanceState(Bundle outState) {
+    public void onSaveInstanceState(@NonNull Bundle outState) {
         super.onSaveInstanceState(outState);
         if (mDialog != null) {
             Bundle dialogState = mDialog.onSaveInstanceState();
diff --git a/graphics/drawable/animated/build.gradle b/graphics/drawable/animated/build.gradle
index 2f18053..ffaceff 100644
--- a/graphics/drawable/animated/build.gradle
+++ b/graphics/drawable/animated/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/graphics/drawable/static/build.gradle b/graphics/drawable/static/build.gradle
index 9388480..63282c0 100644
--- a/graphics/drawable/static/build.gradle
+++ b/graphics/drawable/static/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/heifwriter/build.gradle b/heifwriter/build.gradle
index b66d8a8..133a926 100644
--- a/heifwriter/build.gradle
+++ b/heifwriter/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/interpolator/build.gradle b/interpolator/build.gradle
index 6bc6e04..49c31e2 100644
--- a/interpolator/build.gradle
+++ b/interpolator/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/jetifier/jetifier/core/build.gradle b/jetifier/jetifier/core/build.gradle
index 683a020..5e4f5c0 100644
--- a/jetifier/jetifier/core/build.gradle
+++ b/jetifier/jetifier/core/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License
  */
 
-import static android.support.dependencies.DependenciesKt.KOTLIN_STDLIB
+import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
 
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportKotlinLibraryPlugin")
diff --git a/jetifier/jetifier/gradle-plugin/build.gradle b/jetifier/jetifier/gradle-plugin/build.gradle
index 4a8d0fb..a2b1cfc 100644
--- a/jetifier/jetifier/gradle-plugin/build.gradle
+++ b/jetifier/jetifier/gradle-plugin/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License
  */
 
-import static android.support.dependencies.DependenciesKt.KOTLIN_STDLIB
+import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
 
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
   id("SupportKotlinLibraryPlugin")
diff --git a/leanback/build.gradle b/leanback/build.gradle
index 36ba9e2..da4b168 100644
--- a/leanback/build.gradle
+++ b/leanback/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/lifecycle/common-java8/build.gradle b/lifecycle/common-java8/build.gradle
index bee8b9c..0f4ffd8 100644
--- a/lifecycle/common-java8/build.gradle
+++ b/lifecycle/common-java8/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions;
-import android.support.SupportLibraryExtension;
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions;
+import androidx.build.SupportLibraryExtension;
 
 plugins {
     id("SupportJavaLibraryPlugin")
diff --git a/lifecycle/common/build.gradle b/lifecycle/common/build.gradle
index 866a656..89f46a7 100644
--- a/lifecycle/common/build.gradle
+++ b/lifecycle/common/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions;
-import android.support.SupportLibraryExtension;
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions;
+import androidx.build.SupportLibraryExtension;
 
 plugins {
     id("SupportJavaLibraryPlugin")
diff --git a/lifecycle/compiler/build.gradle b/lifecycle/compiler/build.gradle
index 534d1ec..fec3d99 100644
--- a/lifecycle/compiler/build.gradle
+++ b/lifecycle/compiler/build.gradle
@@ -1,8 +1,11 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
-apply plugin: android.support.SupportKotlinLibraryPlugin
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+    id("SupportKotlinLibraryPlugin")
+}
 
 sourceSets {
     test.java.srcDirs += 'src/tests/kotlin'
diff --git a/lifecycle/extensions/build.gradle b/lifecycle/extensions/build.gradle
index e8ab2f6..2875ab4 100644
--- a/lifecycle/extensions/build.gradle
+++ b/lifecycle/extensions/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/lifecycle/integration-tests/testapp/build.gradle b/lifecycle/integration-tests/testapp/build.gradle
index 25b5b75..c8fba58 100644
--- a/lifecycle/integration-tests/testapp/build.gradle
+++ b/lifecycle/integration-tests/testapp/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("SupportAndroidTestAppPlugin")
diff --git a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java
index 836cfff..d5ba9df 100644
--- a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java
+++ b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java
@@ -116,8 +116,8 @@
         mActivityTestRule.runOnUiThread(() -> mutableLiveData.setValue(0));
 
         TestUtils.waitTillResumed(lifecycleOwner, mActivityTestRule);
-
-        mutableLiveData.observe(lifecycleOwner, atomicInteger::set);
+        mActivityTestRule.runOnUiThread(() ->
+                mutableLiveData.observe(lifecycleOwner, atomicInteger::set));
 
         final FragmentActivity dialogActivity = launchDialog();
 
@@ -143,7 +143,8 @@
 
         TestUtils.waitTillResumed(lifecycleOwner, mActivityTestRule);
 
-        mutableLiveData.observe(lifecycleOwner, atomicInteger::set);
+        mActivityTestRule.runOnUiThread(() ->
+                mutableLiveData.observe(lifecycleOwner, atomicInteger::set));
 
         // Launch the NavigationDialogActivity, partially obscuring the activity, and wait for the
         // lifecycleOwner to hit onPause (or enter the STARTED state).  On API 24 and above, this
diff --git a/lifecycle/livedata-core/build.gradle b/lifecycle/livedata-core/build.gradle
index 58765a0..becb17e 100644
--- a/lifecycle/livedata-core/build.gradle
+++ b/lifecycle/livedata-core/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/lifecycle/livedata-core/src/main/java/android/arch/lifecycle/LiveData.java b/lifecycle/livedata-core/src/main/java/android/arch/lifecycle/LiveData.java
index 3a753a1..fc8fb31 100644
--- a/lifecycle/livedata-core/src/main/java/android/arch/lifecycle/LiveData.java
+++ b/lifecycle/livedata-core/src/main/java/android/arch/lifecycle/LiveData.java
@@ -163,6 +163,7 @@
      */
     @MainThread
     public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
+        assertMainThread("observe");
         if (owner.getLifecycle().getCurrentState() == DESTROYED) {
             // ignore
             return;
@@ -195,6 +196,7 @@
      */
     @MainThread
     public void observeForever(@NonNull Observer<T> observer) {
+        assertMainThread("observeForever");
         AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
         ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
         if (existing != null && existing instanceof LiveData.LifecycleBoundObserver) {
diff --git a/lifecycle/livedata/build.gradle b/lifecycle/livedata/build.gradle
index 7047886..cf3b50c 100644
--- a/lifecycle/livedata/build.gradle
+++ b/lifecycle/livedata/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/lifecycle/livedata/src/test/java/android/arch/lifecycle/ComputableLiveDataTest.java b/lifecycle/livedata/src/test/java/android/arch/lifecycle/ComputableLiveDataTest.java
index d0ff1b2..9130b25 100644
--- a/lifecycle/livedata/src/test/java/android/arch/lifecycle/ComputableLiveDataTest.java
+++ b/lifecycle/livedata/src/test/java/android/arch/lifecycle/ComputableLiveDataTest.java
@@ -98,9 +98,14 @@
             };
             final ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
             //noinspection unchecked
-            Observer<Integer> observer = mock(Observer.class);
-            computable.getLiveData().observeForever(observer);
-            verify(observer, never()).onChanged(anyInt());
+            final Observer<Integer> observer = mock(Observer.class);
+            executor.postToMainThread(new Runnable() {
+                @Override
+                public void run() {
+                    computable.getLiveData().observeForever(observer);
+                    verify(observer, never()).onChanged(anyInt());
+                }
+            });
             // wait for first compute call
             assertThat(computeCounter.tryAcquire(1, 2, TimeUnit.SECONDS), is(true));
             // re-invalidate while in compute
diff --git a/lifecycle/livedata/src/test/java/android/arch/lifecycle/MediatorLiveDataTest.java b/lifecycle/livedata/src/test/java/android/arch/lifecycle/MediatorLiveDataTest.java
index e2eadbe..9dbeb44 100644
--- a/lifecycle/livedata/src/test/java/android/arch/lifecycle/MediatorLiveDataTest.java
+++ b/lifecycle/livedata/src/test/java/android/arch/lifecycle/MediatorLiveDataTest.java
@@ -26,10 +26,12 @@
 import static org.mockito.Mockito.when;
 
 import android.arch.core.executor.ArchTaskExecutor;
+import android.arch.core.executor.testing.InstantTaskExecutorRule;
 import android.arch.lifecycle.util.InstantTaskExecutor;
 import android.support.annotation.Nullable;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -38,6 +40,9 @@
 @RunWith(JUnit4.class)
 public class MediatorLiveDataTest {
 
+    @Rule
+    public InstantTaskExecutorRule mInstantTaskExecutorRule = new InstantTaskExecutorRule();
+
     private LifecycleOwner mOwner;
     private LifecycleRegistry mRegistry;
     private MediatorLiveData<String> mMediator;
diff --git a/lifecycle/reactivestreams/build.gradle b/lifecycle/reactivestreams/build.gradle
index 5c22e4a..12f12b1 100644
--- a/lifecycle/reactivestreams/build.gradle
+++ b/lifecycle/reactivestreams/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/lifecycle/runtime/build.gradle b/lifecycle/runtime/build.gradle
index aee73c0..f7f6ff8 100644
--- a/lifecycle/runtime/build.gradle
+++ b/lifecycle/runtime/build.gradle
@@ -1,7 +1,7 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/lifecycle/viewmodel/build.gradle b/lifecycle/viewmodel/build.gradle
index 914d9ce..1494733 100644
--- a/lifecycle/viewmodel/build.gradle
+++ b/lifecycle/viewmodel/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/loader/build.gradle b/loader/build.gradle
index 45cad2a..b89ac0c 100644
--- a/loader/build.gradle
+++ b/loader/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/localbroadcastmanager/build.gradle b/localbroadcastmanager/build.gradle
index a3d35bf..2709f7d 100644
--- a/localbroadcastmanager/build.gradle
+++ b/localbroadcastmanager/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/media-compat/build.gradle b/media-compat/build.gradle
index 29c91b7..f952731 100644
--- a/media-compat/build.gradle
+++ b/media-compat/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
@@ -14,7 +14,7 @@
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation project(':support-testutils')
+    androidTestImplementation project(':internal-testutils')
 }
 
 android {
diff --git a/media-compat/version-compat-tests/current/client/build.gradle b/media-compat/version-compat-tests/current/client/build.gradle
index 2a247df..385462f 100644
--- a/media-compat/version-compat-tests/current/client/build.gradle
+++ b/media-compat/version-compat-tests/current/client/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/media-compat/version-compat-tests/current/service/build.gradle b/media-compat/version-compat-tests/current/service/build.gradle
index 2a247df..385462f 100644
--- a/media-compat/version-compat-tests/current/service/build.gradle
+++ b/media-compat/version-compat-tests/current/service/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/media-compat/version-compat-tests/lib/build.gradle b/media-compat/version-compat-tests/lib/build.gradle
index caa6c7e..df9e75c 100644
--- a/media-compat/version-compat-tests/lib/build.gradle
+++ b/media-compat/version-compat-tests/lib/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/media-compat/version-compat-tests/previous/client/build.gradle b/media-compat/version-compat-tests/previous/client/build.gradle
index 31f33fe..4319f69 100644
--- a/media-compat/version-compat-tests/previous/client/build.gradle
+++ b/media-compat/version-compat-tests/previous/client/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/media-compat/version-compat-tests/previous/service/build.gradle b/media-compat/version-compat-tests/previous/service/build.gradle
index 765e406..28c88c3 100644
--- a/media-compat/version-compat-tests/previous/service/build.gradle
+++ b/media-compat/version-compat-tests/previous/service/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/paging/common/build.gradle b/paging/common/build.gradle
index dbe6b06..bc1d3a8 100644
--- a/paging/common/build.gradle
+++ b/paging/common/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension;
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension;
 
 plugins {
     id("SupportJavaLibraryPlugin")
diff --git a/paging/integration-tests/testapp/build.gradle b/paging/integration-tests/testapp/build.gradle
index d1b0aec..9aab333 100644
--- a/paging/integration-tests/testapp/build.gradle
+++ b/paging/integration-tests/testapp/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.*
 
 
 plugins {
diff --git a/paging/runtime/build.gradle b/paging/runtime/build.gradle
index 4c42b30..20a39d4 100644
--- a/paging/runtime/build.gradle
+++ b/paging/runtime/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/percent/build.gradle b/percent/build.gradle
index 9585323..7209ac2 100644
--- a/percent/build.gradle
+++ b/percent/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/persistence/db-framework/build.gradle b/persistence/db-framework/build.gradle
index bae3d02..15f638b 100644
--- a/persistence/db-framework/build.gradle
+++ b/persistence/db-framework/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/persistence/db/api/current.txt b/persistence/db/api/current.txt
index f96f17a..e0abe20 100644
--- a/persistence/db/api/current.txt
+++ b/persistence/db/api/current.txt
@@ -5,6 +5,7 @@
     ctor public SimpleSQLiteQuery(java.lang.String);
     method public static void bind(android.arch.persistence.db.SupportSQLiteProgram, java.lang.Object[]);
     method public void bindTo(android.arch.persistence.db.SupportSQLiteProgram);
+    method public int getArgCount();
     method public java.lang.String getSql();
   }
 
@@ -96,6 +97,7 @@
 
   public abstract interface SupportSQLiteQuery {
     method public abstract void bindTo(android.arch.persistence.db.SupportSQLiteProgram);
+    method public abstract int getArgCount();
     method public abstract java.lang.String getSql();
   }
 
diff --git a/persistence/db/build.gradle b/persistence/db/build.gradle
index 657ef1e..5c9cb2d 100644
--- a/persistence/db/build.gradle
+++ b/persistence/db/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/persistence/db/src/main/java/android/arch/persistence/db/SimpleSQLiteQuery.java b/persistence/db/src/main/java/android/arch/persistence/db/SimpleSQLiteQuery.java
index bcf4f49..13faf24 100644
--- a/persistence/db/src/main/java/android/arch/persistence/db/SimpleSQLiteQuery.java
+++ b/persistence/db/src/main/java/android/arch/persistence/db/SimpleSQLiteQuery.java
@@ -54,6 +54,11 @@
         bind(statement, mBindArgs);
     }
 
+    @Override
+    public int getArgCount() {
+        return mBindArgs.length;
+    }
+
     /**
      * Binds the given arguments into the given sqlite statement.
      *
@@ -91,6 +96,8 @@
             statement.bindLong(index, (Byte) arg);
         } else if (arg instanceof String) {
             statement.bindString(index, (String) arg);
+        } else if (arg instanceof Boolean) {
+            statement.bindLong(index, ((Boolean) arg) ? 1 : 0);
         } else {
             throw new IllegalArgumentException("Cannot bind " + arg + " at index " + index
                     + " Supported types: null, byte[], float, double, long, int, short, byte,"
diff --git a/persistence/db/src/main/java/android/arch/persistence/db/SupportSQLiteQuery.java b/persistence/db/src/main/java/android/arch/persistence/db/SupportSQLiteQuery.java
index 2007634..03e0a91 100644
--- a/persistence/db/src/main/java/android/arch/persistence/db/SupportSQLiteQuery.java
+++ b/persistence/db/src/main/java/android/arch/persistence/db/SupportSQLiteQuery.java
@@ -35,4 +35,12 @@
      * @param statement The compiled statement
      */
     void bindTo(SupportSQLiteProgram statement);
+
+    /**
+     * Returns the number of arguments in this query. This is equal to the number of placeholders
+     * in the query string. See: https://www.sqlite.org/c3ref/bind_blob.html for details.
+     *
+     * @return The number of arguments in the query.
+     */
+    int getArgCount();
 }
diff --git a/preference-leanback/build.gradle b/preference-leanback/build.gradle
index fc88f28..e5c19da 100644
--- a/preference-leanback/build.gradle
+++ b/preference-leanback/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/print/build.gradle b/print/build.gradle
index 78fc9db..d3af888 100644
--- a/print/build.gradle
+++ b/print/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/print/src/main/java/android/support/v4/print/PrintHelper.java b/print/src/main/java/android/support/v4/print/PrintHelper.java
index ce342e3..5f445b7 100644
--- a/print/src/main/java/android/support/v4/print/PrintHelper.java
+++ b/print/src/main/java/android/support/v4/print/PrintHelper.java
@@ -39,6 +39,8 @@
 import android.print.PrintManager;
 import android.print.pdf.PrintedPdfDocument;
 import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.annotation.RequiresApi;
 import android.util.Log;
 
@@ -136,9 +138,11 @@
 
         int getOrientation();
 
-        void printBitmap(String jobName, Bitmap bitmap, OnPrintFinishCallback callback);
+        void printBitmap(@NonNull String jobName, @NonNull Bitmap bitmap,
+                @Nullable OnPrintFinishCallback callback);
 
-        void printBitmap(String jobName, Uri imageFile, OnPrintFinishCallback callback)
+        void printBitmap(@NonNull String jobName, @NonNull Uri imageFile,
+                @Nullable OnPrintFinishCallback callback)
                 throws FileNotFoundException;
     }
 
@@ -885,7 +889,7 @@
      *
      * @param context A context for accessing system resources.
      */
-    public PrintHelper(Context context) {
+    public PrintHelper(@NonNull Context context) {
         if (Build.VERSION.SDK_INT >= 24) {
             mImpl = new PrintHelperApi24(context);
         } else if (Build.VERSION.SDK_INT >= 23) {
@@ -975,7 +979,7 @@
      * @param jobName The print job name.
      * @param bitmap  The bitmap to print.
      */
-    public void printBitmap(String jobName, Bitmap bitmap) {
+    public void printBitmap(@NonNull String jobName, @NonNull Bitmap bitmap) {
         mImpl.printBitmap(jobName, bitmap, null);
     }
 
@@ -986,7 +990,8 @@
      * @param bitmap  The bitmap to print.
      * @param callback Optional callback to observe when printing is finished.
      */
-    public void printBitmap(String jobName, Bitmap bitmap, OnPrintFinishCallback callback) {
+    public void printBitmap(@NonNull String jobName, @NonNull Bitmap bitmap,
+            @Nullable OnPrintFinishCallback callback) {
         mImpl.printBitmap(jobName, bitmap, callback);
     }
 
@@ -999,7 +1004,8 @@
      * @param imageFile The <code>Uri</code> pointing to an image to print.
      * @throws FileNotFoundException if <code>Uri</code> is not pointing to a valid image.
      */
-    public void printBitmap(String jobName, Uri imageFile) throws FileNotFoundException {
+    public void printBitmap(@NonNull String jobName, @NonNull Uri imageFile)
+            throws FileNotFoundException {
         mImpl.printBitmap(jobName, imageFile, null);
     }
 
@@ -1013,7 +1019,8 @@
      * @throws FileNotFoundException if <code>Uri</code> is not pointing to a valid image.
      * @param callback Optional callback to observe when printing is finished.
      */
-    public void printBitmap(String jobName, Uri imageFile, OnPrintFinishCallback callback)
+    public void printBitmap(@NonNull String jobName, @NonNull Uri imageFile,
+            @Nullable OnPrintFinishCallback callback)
             throws FileNotFoundException {
         mImpl.printBitmap(jobName, imageFile, callback);
     }
diff --git a/recommendation/build.gradle b/recommendation/build.gradle
index b401eb8..4f4a737 100644
--- a/recommendation/build.gradle
+++ b/recommendation/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/recyclerview-selection/build.gradle b/recyclerview-selection/build.gradle
index c0915bc..6db338f 100644
--- a/recyclerview-selection/build.gradle
+++ b/recyclerview-selection/build.gradle
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/room/common/build.gradle b/room/common/build.gradle
index 9457308..1bf6652 100644
--- a/room/common/build.gradle
+++ b/room/common/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension;
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension;
 
 plugins {
     id("SupportJavaLibraryPlugin")
diff --git a/room/common/src/main/java/android/arch/persistence/room/ColumnInfo.java b/room/common/src/main/java/android/arch/persistence/room/ColumnInfo.java
index 32b5818..8d0b7ce 100644
--- a/room/common/src/main/java/android/arch/persistence/room/ColumnInfo.java
+++ b/room/common/src/main/java/android/arch/persistence/room/ColumnInfo.java
@@ -17,6 +17,7 @@
 package android.arch.persistence.room;
 
 import android.support.annotation.IntDef;
+import android.support.annotation.RequiresApi;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -146,12 +147,14 @@
      *
      * @see #collate()
      */
+    @RequiresApi(21)
     int LOCALIZED = 5;
     /**
      * Collation sequence that uses Unicode Collation Algorithm.
      *
      * @see #collate()
      */
+    @RequiresApi(21)
     int UNICODE = 6;
 
     @IntDef({UNSPECIFIED, BINARY, NOCASE, RTRIM, LOCALIZED, UNICODE})
diff --git a/room/compiler/build.gradle b/room/compiler/build.gradle
index c3319ac..3599893 100644
--- a/room/compiler/build.gradle
+++ b/room/compiler/build.gradle
@@ -15,14 +15,16 @@
  */
 
 
-import android.support.SupportConfig
+import androidx.build.SupportConfig
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
-apply plugin: android.support.SupportKotlinLibraryPlugin
+plugins {
+    id("SupportKotlinLibraryPlugin")
+}
 
 def antlrOut = "$buildDir/generated/antlr/grammar-gen/"
 sourceSets {
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ProcessorErrors.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ProcessorErrors.kt
index 02e299b..3e7dd9c 100644
--- a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ProcessorErrors.kt
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ProcessorErrors.kt
@@ -146,9 +146,10 @@
             " queries."
 
     val OBSERVABLE_QUERY_NOTHING_TO_OBSERVE = "Observable query return type (LiveData, Flowable" +
-            " etc) can only be used with SELECT queries that directly or indirectly (via" +
-            " @Relation, for example) access at least one table. For @RawQuery, you should" +
-            " specify the list of tables to be observed via the observedEntities field."
+            ", DataSource, DataSourceFactory etc) can only be used with SELECT queries that" +
+            " directly or indirectly (via @Relation, for example) access at least one table. For" +
+            " @RawQuery, you should specify the list of tables to be observed via the" +
+            " observedEntities field."
 
     val RECURSIVE_REFERENCE_DETECTED = "Recursive referencing through @Embedded and/or @Relation " +
             "detected: %s"
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/DataSourceFactoryQueryResultBinderProvider.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/DataSourceFactoryQueryResultBinderProvider.kt
index 4d0a280..dc11428 100644
--- a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/DataSourceFactoryQueryResultBinderProvider.kt
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/DataSourceFactoryQueryResultBinderProvider.kt
@@ -19,10 +19,11 @@
 import android.arch.persistence.room.ext.PagingTypeNames
 import android.arch.persistence.room.parser.ParsedQuery
 import android.arch.persistence.room.processor.Context
+import android.arch.persistence.room.processor.ProcessorErrors
 import android.arch.persistence.room.solver.QueryResultBinderProvider
-import android.arch.persistence.room.solver.query.result.PositionalDataSourceQueryResultBinder
 import android.arch.persistence.room.solver.query.result.ListQueryResultAdapter
 import android.arch.persistence.room.solver.query.result.LivePagedListQueryResultBinder
+import android.arch.persistence.room.solver.query.result.PositionalDataSourceQueryResultBinder
 import android.arch.persistence.room.solver.query.result.QueryResultBinder
 import javax.lang.model.type.DeclaredType
 import javax.lang.model.type.TypeMirror
@@ -34,6 +35,9 @@
     }
 
     override fun provide(declared: DeclaredType, query: ParsedQuery): QueryResultBinder {
+        if (query.tables.isEmpty()) {
+            context.logger.e(ProcessorErrors.OBSERVABLE_QUERY_NOTHING_TO_OBSERVE)
+        }
         val typeArg = declared.typeArguments[1]
         val listAdapter = context.typeAdapterStore.findRowAdapter(typeArg, query)?.let {
             ListQueryResultAdapter(it)
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/DataSourceQueryResultBinderProvider.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/DataSourceQueryResultBinderProvider.kt
index c13354e..0fc9dce 100644
--- a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/DataSourceQueryResultBinderProvider.kt
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/binderprovider/DataSourceQueryResultBinderProvider.kt
@@ -39,6 +39,9 @@
     }
 
     override fun provide(declared: DeclaredType, query: ParsedQuery): QueryResultBinder {
+        if (query.tables.isEmpty()) {
+            context.logger.e(ProcessorErrors.OBSERVABLE_QUERY_NOTHING_TO_OBSERVE)
+        }
         val typeArg = declared.typeArguments.last()
         val listAdapter = context.typeAdapterStore.findRowAdapter(typeArg, query)?.let {
             ListQueryResultAdapter(it)
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
index ac5a256..4530332 100644
--- a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
@@ -42,8 +42,11 @@
                                   dbField: FieldSpec,
                                   inTransaction: Boolean,
                                   scope: CodeGenScope) {
-        val tableNamesList = tableNames.joinToString(",") { "\"$it\"" }
-        val spec = TypeSpec.anonymousClassBuilder("$N, $L, $L, $L",
+        // first comma for table names comes from the string since it might be empty in which case
+        // we don't need a comma. If list is empty, this prevents generating bad code (it is still
+        // an error to have empty list but that is already reported while item is processed)
+        val tableNamesList = tableNames.joinToString { ",\"$it\"" }
+        val spec = TypeSpec.anonymousClassBuilder("$N, $L, $L $L",
                 dbField, roomSQLiteQueryVar, inTransaction, tableNamesList).apply {
             superclass(typeName)
             addMethod(createConvertRowsMethod(scope))
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Relation.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Relation.kt
index 95130e8..512770a 100644
--- a/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Relation.kt
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/vo/Relation.kt
@@ -44,8 +44,8 @@
     }
 
     private fun createSelect(resultFields: Set<String>): String {
-        return "SELECT ${resultFields.joinToString(",")}" +
+        return "SELECT ${resultFields.joinToString(",") {"`$it`"}}" +
                 " FROM `${entity.tableName}`" +
-                " WHERE ${entityField.columnName} IN (:args)"
+                " WHERE `${entityField.columnName}` IN (:args)"
     }
 }
diff --git a/room/compiler/src/test/data/common/input/PositionalDataSource.java b/room/compiler/src/test/data/common/input/PositionalDataSource.java
new file mode 100644
index 0000000..7cff0d7
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/PositionalDataSource.java
@@ -0,0 +1,5 @@
+package android.arch.paging;
+
+public abstract class PositionalDataSource<T> extends DataSource<Integer, T> {
+
+}
\ No newline at end of file
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/RawQueryMethodProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/RawQueryMethodProcessorTest.kt
index 3eb629a..17e8ad9 100644
--- a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/RawQueryMethodProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/RawQueryMethodProcessorTest.kt
@@ -24,6 +24,7 @@
 import android.arch.persistence.room.Query
 import android.arch.persistence.room.RawQuery
 import android.arch.persistence.room.ext.CommonTypeNames
+import android.arch.persistence.room.ext.PagingTypeNames
 import android.arch.persistence.room.ext.SupportDbTypeNames
 import android.arch.persistence.room.ext.hasAnnotation
 import android.arch.persistence.room.ext.typeName
@@ -123,6 +124,42 @@
     }
 
     @Test
+    fun observableWithoutEntities_dataSourceFactory() {
+        singleQueryMethod(
+                """
+                @RawQuery
+                abstract public ${PagingTypeNames.DATA_SOURCE_FACTORY}<Integer, User> getOne();
+                """) { _, _ ->
+            // do nothing
+        }.failsToCompile()
+                .withErrorContaining(ProcessorErrors.OBSERVABLE_QUERY_NOTHING_TO_OBSERVE)
+    }
+
+    @Test
+    fun observableWithoutEntities_positionalDataSource() {
+        singleQueryMethod(
+                """
+                @RawQuery
+                abstract public ${PagingTypeNames.POSITIONAL_DATA_SOURCE}<User> getOne();
+                """) { _, _ ->
+            // do nothing
+        }.failsToCompile()
+                .withErrorContaining(ProcessorErrors.OBSERVABLE_QUERY_NOTHING_TO_OBSERVE)
+    }
+
+    @Test
+    fun positionalDataSource() {
+        singleQueryMethod(
+                """
+                @RawQuery(observedEntities = {User.class})
+                abstract public ${PagingTypeNames.POSITIONAL_DATA_SOURCE}<User> getOne(
+                        SupportSQLiteQuery query);
+                """) { _, _ ->
+            // do nothing
+        }.compilesWithoutError()
+    }
+
+    @Test
     fun pojo() {
         val pojo: TypeName = ClassName.get("foo.bar.MyClass", "MyPojo")
         singleQueryMethod(
@@ -204,10 +241,11 @@
                         DAO_PREFIX
                                 + input.joinToString("\n")
                                 + DAO_SUFFIX
-                ), COMMON.LIVE_DATA, COMMON.COMPUTABLE_LIVE_DATA, COMMON.USER))
+                ), COMMON.LIVE_DATA, COMMON.COMPUTABLE_LIVE_DATA, COMMON.USER,
+                        COMMON.DATA_SOURCE_FACTORY, COMMON.POSITIONAL_DATA_SOURCE))
                 .processedWith(TestProcessor.builder()
                         .forAnnotations(Query::class, Dao::class, ColumnInfo::class,
-                                Entity::class, PrimaryKey::class, RawQueryMethod::class)
+                                Entity::class, PrimaryKey::class, RawQuery::class)
                         .nextRunHandler { invocation ->
                             val (owner, methods) = invocation.roundEnv
                                     .getElementsAnnotatedWith(Dao::class.java)
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/test_util.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/test_util.kt
index b657643..c0167b3 100644
--- a/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/test_util.kt
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/testing/test_util.kt
@@ -21,6 +21,7 @@
 import android.arch.persistence.room.Query
 import android.arch.persistence.room.Relation
 import android.arch.persistence.room.ext.LifecyclesTypeNames
+import android.arch.persistence.room.ext.PagingTypeNames
 import android.arch.persistence.room.ext.ReactiveStreamsTypeNames
 import android.arch.persistence.room.ext.RoomRxJava2TypeNames
 import android.arch.persistence.room.ext.RxJava2TypeNames
@@ -88,6 +89,11 @@
     val DATA_SOURCE_FACTORY by lazy {
         loadJavaCode("common/input/DataSource.java", "android.arch.paging.DataSource")
     }
+
+    val POSITIONAL_DATA_SOURCE by lazy {
+        loadJavaCode("common/input/PositionalDataSource.java",
+                PagingTypeNames.POSITIONAL_DATA_SOURCE.toString())
+    }
 }
 fun testCodeGenScope(): CodeGenScope {
     return CodeGenScope(Mockito.mock(ClassWriter::class.java))
diff --git a/room/guava/build.gradle b/room/guava/build.gradle
index 8660aae..3837406 100644
--- a/room/guava/build.gradle
+++ b/room/guava/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/room/integration-tests/kotlintestapp/build.gradle b/room/integration-tests/kotlintestapp/build.gradle
index 1dc0a79..162f325 100644
--- a/room/integration-tests/kotlintestapp/build.gradle
+++ b/room/integration-tests/kotlintestapp/build.gradle
@@ -17,7 +17,7 @@
 //./gradlew :r:in:k:clean :r:in:k:cC --no-daemon
 // -Dorg.gradle.debug=true
 // -Dkotlin.compiler.execution.strategy="in-process"
-import static android.support.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("SupportAndroidTestAppPlugin")
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
index 45ec9c6..401cc07 100644
--- a/room/integration-tests/testapp/build.gradle
+++ b/room/integration-tests/testapp/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("SupportAndroidTestAppPlugin")
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/UserDao.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/UserDao.java
index 7cb8b60..1db5b46 100644
--- a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/UserDao.java
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/UserDao.java
@@ -18,11 +18,13 @@
 
 import android.arch.lifecycle.LiveData;
 import android.arch.paging.DataSource;
+import android.arch.persistence.db.SupportSQLiteQuery;
 import android.arch.persistence.room.Dao;
 import android.arch.persistence.room.Delete;
 import android.arch.persistence.room.Insert;
 import android.arch.persistence.room.OnConflictStrategy;
 import android.arch.persistence.room.Query;
+import android.arch.persistence.room.RawQuery;
 import android.arch.persistence.room.Transaction;
 import android.arch.persistence.room.Update;
 import android.arch.persistence.room.integration.testapp.TestDatabase;
@@ -116,6 +118,10 @@
     @Query("select * from user where mId = :id")
     public abstract LiveData<User> liveUserById(int id);
 
+    @Transaction
+    @Query("select * from user where mId = :id")
+    public abstract LiveData<User> liveUserByIdInTransaction(int id);
+
     @Query("select * from user where mName LIKE '%' || :name || '%' ORDER BY mId DESC")
     public abstract LiveData<List<User>> liveUsersListByName(String name);
 
@@ -194,6 +200,10 @@
     @Query("SELECT * FROM user where mAge > :age")
     public abstract DataSource.Factory<Integer, User> loadPagedByAge(int age);
 
+    @RawQuery(observedEntities = User.class)
+    public abstract DataSource.Factory<Integer, User> loadPagedByAgeWithObserver(
+            SupportSQLiteQuery query);
+
     // TODO: switch to PositionalDataSource once Room supports it
     @Query("SELECT * FROM user ORDER BY mAge DESC")
     public abstract DataSource.Factory<Integer, User> loadUsersByAgeDesc();
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/paging/DataSourceFactoryTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/paging/DataSourceFactoryTest.java
index 0f68656..587367f 100644
--- a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/paging/DataSourceFactoryTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/paging/DataSourceFactoryTest.java
@@ -30,6 +30,7 @@
 import android.arch.lifecycle.Observer;
 import android.arch.paging.LivePagedListBuilder;
 import android.arch.paging.PagedList;
+import android.arch.persistence.db.SimpleSQLiteQuery;
 import android.arch.persistence.room.integration.testapp.test.TestDatabaseTest;
 import android.arch.persistence.room.integration.testapp.test.TestUtil;
 import android.arch.persistence.room.integration.testapp.vo.User;
@@ -41,7 +42,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
@@ -69,7 +69,23 @@
                 .build());
     }
 
-    private void validateUsersAsPagedList(LivePagedListFactory factory)
+    @Test
+    public void getUsersAsPagedList_ViaRawQuery_WithObservable()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        SimpleSQLiteQuery query = new SimpleSQLiteQuery(
+                "SELECT * FROM user where mAge > ?",
+                new Object[]{3});
+        validateUsersAsPagedList(() -> new LivePagedListBuilder<>(
+                mUserDao.loadPagedByAgeWithObserver(query),
+                new PagedList.Config.Builder()
+                        .setPageSize(10)
+                        .setPrefetchDistance(1)
+                        .setInitialLoadSizeHint(10).build())
+                .build());
+    }
+
+    private void validateUsersAsPagedList(
+            LivePagedListFactory factory)
             throws InterruptedException, ExecutionException, TimeoutException {
         mDatabase.beginTransaction();
         try {
@@ -135,13 +151,10 @@
 
     private void observe(final LiveData liveData, final LifecycleOwner provider,
             final Observer observer) throws ExecutionException, InterruptedException {
-        FutureTask<Void> futureTask = new FutureTask<>(new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                //noinspection unchecked
-                liveData.observe(provider, observer);
-                return null;
-            }
+        FutureTask<Void> futureTask = new FutureTask<>(() -> {
+            //noinspection unchecked
+            liveData.observe(provider, observer);
+            return null;
         });
         ArchTaskExecutor.getInstance().executeOnMainThread(futureTask);
         futureTask.get();
@@ -167,6 +180,7 @@
 
     private static class PagedListObserver<T> implements Observer<PagedList<T>> {
         private PagedList<T> mList;
+
         void reset() {
             mList = null;
         }
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/CollationTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/CollationTest.java
index 7b0e933..f81cb70 100644
--- a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/CollationTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/CollationTest.java
@@ -27,6 +27,7 @@
 import android.arch.persistence.room.Room;
 import android.arch.persistence.room.RoomDatabase;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -83,6 +84,7 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = 21)
     public void localized_asUnicode() {
         initDao(Locale.getDefault());
         List<CollateEntity> result = mDao.sortedByLocalizedAsUnicode();
@@ -101,6 +103,7 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = 21)
     public void unicode() {
         initDao(Locale.getDefault());
         List<CollateEntity> result = mDao.sortedByUnicode();
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/DaoNameConflictTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/DaoNameConflictTest.java
index 1b8bff4..07c0369 100644
--- a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/DaoNameConflictTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/DaoNameConflictTest.java
@@ -36,8 +36,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Objects;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class DaoNameConflictTest {
@@ -89,14 +87,17 @@
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             Item1 item1 = (Item1) o;
-            return id == item1.id
-                    && Objects.equals(name, item1.name);
+
+            //noinspection SimplifiableIfStatement
+            if (id != item1.id) return false;
+            return name != null ? name.equals(item1.name) : item1.name == null;
         }
 
         @Override
         public int hashCode() {
-
-            return Objects.hash(id, name);
+            int result = id;
+            result = 31 * result + (name != null ? name.hashCode() : 0);
+            return result;
         }
     }
 
@@ -124,14 +125,17 @@
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             Item2 item2 = (Item2) o;
-            return id == item2.id
-                    && Objects.equals(name, item2.name);
+
+            //noinspection SimplifiableIfStatement
+            if (id != item2.id) return false;
+            return name != null ? name.equals(item2.name) : item2.name == null;
         }
 
         @Override
         public int hashCode() {
-
-            return Objects.hash(id, name);
+            int result = id;
+            result = 31 * result + (name != null ? name.hashCode() : 0);
+            return result;
         }
     }
 
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/FunnyNamedDaoTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/FunnyNamedDaoTest.java
index f4fca7f..dcefa41 100644
--- a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/FunnyNamedDaoTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/FunnyNamedDaoTest.java
@@ -22,9 +22,8 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import android.arch.core.executor.testing.CountingTaskExecutorRule;
-import android.arch.lifecycle.Observer;
 import android.arch.persistence.room.integration.testapp.vo.FunnyNamedEntity;
-import android.support.annotation.Nullable;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -71,12 +70,9 @@
     @Test
     public void observe() throws TimeoutException, InterruptedException {
         final FunnyNamedEntity[] item = new FunnyNamedEntity[1];
-        mFunnyNamedDao.observableOne(2).observeForever(new Observer<FunnyNamedEntity>() {
-            @Override
-            public void onChanged(@Nullable FunnyNamedEntity funnyNamedEntity) {
-                item[0] = funnyNamedEntity;
-            }
-        });
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                () -> mFunnyNamedDao.observableOne(2).observeForever(
+                        funnyNamedEntity -> item[0] = funnyNamedEntity));
 
         FunnyNamedEntity entity = new FunnyNamedEntity(1, "a");
         mFunnyNamedDao.insert(entity);
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/GenericEntityTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/GenericEntityTest.java
index 6db28d4..8efb7c7 100644
--- a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/GenericEntityTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/GenericEntityTest.java
@@ -37,8 +37,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Objects;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class GenericEntityTest {
@@ -98,14 +96,16 @@
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             Item<?, ?> item = (Item<?, ?>) o;
-            return Objects.equals(id, item.id)
-                    && Objects.equals(mField, item.mField);
+            //noinspection SimplifiableIfStatement
+            if (!id.equals(item.id)) return false;
+            return mField != null ? mField.equals(item.mField) : item.mField == null;
         }
 
         @Override
         public int hashCode() {
-
-            return Objects.hash(id, mField);
+            int result = id.hashCode();
+            result = 31 * result + (mField != null ? mField.hashCode() : 0);
+            return result;
         }
     }
 
@@ -128,14 +128,16 @@
         public boolean equals(Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
-            EntityItem item = (EntityItem) o;
-            return Objects.equals(name, item.name);
+            if (!super.equals(o)) return false;
+            EntityItem that = (EntityItem) o;
+            return name != null ? name.equals(that.name) : that.name == null;
         }
 
         @Override
         public int hashCode() {
-
-            return Objects.hash(name);
+            int result = super.hashCode();
+            result = 31 * result + (name != null ? name.hashCode() : 0);
+            return result;
         }
     }
 
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/RawQueryTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/RawQueryTest.java
index 4aae4ea..d2cf37b 100644
--- a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/RawQueryTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/RawQueryTest.java
@@ -29,6 +29,7 @@
 import android.arch.persistence.room.integration.testapp.vo.User;
 import android.arch.persistence.room.integration.testapp.vo.UserAndAllPets;
 import android.arch.persistence.room.integration.testapp.vo.UserAndPet;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -71,9 +72,10 @@
 
     @Test
     public void entity_liveData() throws TimeoutException, InterruptedException {
-        LiveData<User> liveData = mRawDao.getUserLiveData("SELECT * FROM User WHERE mId = 3");
-        liveData.observeForever(user -> {
-        });
+        final LiveData<User> liveData = mRawDao.getUserLiveData("SELECT * FROM User WHERE mId = 3");
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                () -> liveData.observeForever(user -> { }));
+
         drain();
         assertThat(liveData.getValue(), is(nullValue()));
         User user = TestUtil.createUser(3);
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/RelationWithReservedKeywordTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/RelationWithReservedKeywordTest.java
new file mode 100644
index 0000000..b01ed45
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/RelationWithReservedKeywordTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.arch.persistence.room.integration.testapp.test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import static java.util.Collections.singletonList;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Database;
+import android.arch.persistence.room.Embedded;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.ForeignKey;
+import android.arch.persistence.room.Index;
+import android.arch.persistence.room.Insert;
+import android.arch.persistence.room.PrimaryKey;
+import android.arch.persistence.room.Query;
+import android.arch.persistence.room.Relation;
+import android.arch.persistence.room.Room;
+import android.arch.persistence.room.RoomDatabase;
+import android.arch.persistence.room.Transaction;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RelationWithReservedKeywordTest {
+    private MyDatabase mDb;
+
+    @Before
+    public void initDb() {
+        mDb = Room.inMemoryDatabaseBuilder(
+                InstrumentationRegistry.getTargetContext(),
+                MyDatabase.class).build();
+    }
+
+    @Test
+    public void loadRelation() {
+        Category category = new Category(1, "cat1");
+        mDb.getDao().insert(category);
+        Topic topic = new Topic(2, 1, "foo");
+        mDb.getDao().insert(topic);
+        List<CategoryWithTopics> categoryWithTopics = mDb.getDao().loadAll();
+        assertThat(categoryWithTopics.size(), is(1));
+        assertThat(categoryWithTopics.get(0).category, is(category));
+        assertThat(categoryWithTopics.get(0).topics, is(singletonList(topic)));
+    }
+
+    @Entity(tableName = "categories")
+    static class Category {
+
+        @PrimaryKey(autoGenerate = true)
+        public final long id;
+
+        public final String name;
+
+        Category(long id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Category category = (Category) o;
+            //noinspection SimplifiableIfStatement
+            if (id != category.id) return false;
+            return name != null ? name.equals(category.name) : category.name == null;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = (int) (id ^ (id >>> 32));
+            result = 31 * result + (name != null ? name.hashCode() : 0);
+            return result;
+        }
+    }
+
+    @Dao
+    interface MyDao {
+        @Transaction
+        @Query("SELECT * FROM categories")
+        List<CategoryWithTopics> loadAll();
+
+        @Insert
+        void insert(Category... categories);
+
+        @Insert
+        void insert(Topic... topics);
+    }
+
+    @Database(
+            entities = {Category.class, Topic.class},
+            version = 1,
+            exportSchema = false)
+    abstract static class MyDatabase extends RoomDatabase {
+        abstract MyDao getDao();
+    }
+
+
+    @SuppressWarnings("WeakerAccess")
+    static class CategoryWithTopics {
+        @Embedded
+        public Category category;
+
+        @Relation(
+                parentColumn = "id",
+                entityColumn = "category_id",
+                entity = Topic.class)
+        public List<Topic> topics;
+    }
+
+    @Entity(
+            tableName = "topics",
+            foreignKeys = @ForeignKey(
+                    entity = Category.class,
+                    parentColumns = "id",
+                    childColumns = "category_id",
+                    onDelete = ForeignKey.CASCADE),
+            indices = @Index("category_id"))
+    static class Topic {
+
+        @PrimaryKey(autoGenerate = true)
+        public final long id;
+
+        @ColumnInfo(name = "category_id")
+        public final long categoryId;
+
+        public final String to;
+
+        Topic(long id, long categoryId, String to) {
+            this.id = id;
+            this.categoryId = categoryId;
+            this.to = to;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Topic topic = (Topic) o;
+            if (id != topic.id) return false;
+            //noinspection SimplifiableIfStatement
+            if (categoryId != topic.categoryId) return false;
+            return to != null ? to.equals(topic.to) : topic.to == null;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = (int) (id ^ (id >>> 32));
+            result = 31 * result + (int) (categoryId ^ (categoryId >>> 32));
+            result = 31 * result + (to != null ? to.hashCode() : 0);
+            return result;
+        }
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/WriteAheadLoggingTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/WriteAheadLoggingTest.java
index 8d8e0e8..5bc01ce 100644
--- a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/WriteAheadLoggingTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/WriteAheadLoggingTest.java
@@ -17,10 +17,12 @@
 package android.arch.persistence.room.integration.testapp.test;
 
 import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.hasItem;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.equalToIgnoringCase;
+import static org.hamcrest.Matchers.hasSize;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
@@ -39,6 +41,7 @@
 import android.database.Cursor;
 import android.support.annotation.NonNull;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.runner.AndroidJUnit4;
@@ -125,7 +128,18 @@
         LiveData<User> user1 = dao.liveUserById(1);
         Observer<User> observer = startObserver(user1);
         dao.insert(TestUtil.createUser(1));
-        verify(observer, timeout(30000).atLeastOnce())
+        verify(observer, timeout(3000).atLeastOnce())
+                .onChanged(argThat(user -> user != null && user.getId() == 1));
+        stopObserver(user1, observer);
+    }
+
+    @Test
+    public void observeLiveDataWithTransaction() {
+        UserDao dao = mDatabase.getUserDao();
+        LiveData<User> user1 = dao.liveUserByIdInTransaction(1);
+        Observer<User> observer = startObserver(user1);
+        dao.insert(TestUtil.createUser(1));
+        verify(observer, timeout(3000).atLeastOnce())
                 .onChanged(argThat(user -> user != null && user.getId() == 1));
         stopObserver(user1, observer);
     }
@@ -159,15 +173,12 @@
         final UserDao dao = mDatabase.getUserDao();
         final User user1 = TestUtil.createUser(1);
         dao.insert(user1);
-        Future<Boolean> future;
         try {
             mDatabase.beginTransaction();
             dao.delete(user1);
             ExecutorService executor = Executors.newSingleThreadExecutor();
-            future = executor.submit(() -> {
-                assertThat(dao.load(1), is(equalTo(user1)));
-                return true;
-            });
+            Future<?> future = executor.submit(() ->
+                    assertThat(dao.load(1), is(equalTo(user1))));
             future.get();
             mDatabase.setTransactionSuccessful();
         } finally {
@@ -177,21 +188,52 @@
     }
 
     @Test
+    @LargeTest
+    public void observeInvalidationInBackground() throws InterruptedException, ExecutionException {
+        final UserDao dao = mDatabase.getUserDao();
+        final User user1 = TestUtil.createUser(1);
+        final CountDownLatch observerRegistered = new CountDownLatch(1);
+        final CountDownLatch onInvalidatedCalled = new CountDownLatch(1);
+        dao.insert(user1);
+        Future future;
+        try {
+            mDatabase.beginTransaction();
+            dao.delete(user1);
+            future = Executors.newSingleThreadExecutor().submit(() -> {
+                // Adding this observer will be blocked by the surrounding transaction.
+                mDatabase.getInvalidationTracker().addObserver(
+                        new InvalidationTracker.Observer("User") {
+                            @Override
+                            public void onInvalidated(@NonNull Set<String> tables) {
+                                onInvalidatedCalled.countDown(); // This should not happen
+                            }
+                        });
+                observerRegistered.countDown();
+            });
+            mDatabase.setTransactionSuccessful();
+        } finally {
+            assertThat(observerRegistered.getCount(), is(1L));
+            mDatabase.endTransaction();
+        }
+        assertThat(dao.count(), is(0));
+        assertThat(observerRegistered.await(3000, TimeUnit.MILLISECONDS), is(true));
+        future.get();
+        assertThat(onInvalidatedCalled.await(500, TimeUnit.MILLISECONDS), is(false));
+    }
+
+    @Test
     public void invalidation() throws InterruptedException {
         final CountDownLatch latch = new CountDownLatch(1);
         mDatabase.getInvalidationTracker().addObserver(new InvalidationTracker.Observer("User") {
             @Override
             public void onInvalidated(@NonNull Set<String> tables) {
+                assertThat(tables, hasSize(1));
+                assertThat(tables, hasItem("User"));
                 latch.countDown();
             }
         });
         mDatabase.getUserDao().insert(TestUtil.createUser(1));
-        latch.await(3000, TimeUnit.MILLISECONDS);
-        for (int i = 0; i < 10; i++) {
-            // This can (occasionally) detect if there is an recursive loop in InvalidationTracker
-            // invalidating itself by running its refresh query in a transaction.
-            assertThat(mDatabase.inTransaction(), is(false));
-        }
+        assertThat(latch.await(3000, TimeUnit.MILLISECONDS), is(true));
     }
 
     private static <T> Observer<T> startObserver(LiveData<T> liveData) {
diff --git a/room/migration/build.gradle b/room/migration/build.gradle
index acadb71..2b683e8 100644
--- a/room/migration/build.gradle
+++ b/room/migration/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension;
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension;
 
 plugins {
     id("SupportJavaLibraryPlugin")
diff --git a/room/runtime/build.gradle b/room/runtime/build.gradle
index 1022616..739725c 100644
--- a/room/runtime/build.gradle
+++ b/room/runtime/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/InvalidationTracker.java b/room/runtime/src/main/java/android/arch/persistence/room/InvalidationTracker.java
index 86054ce..7bb4fb2 100644
--- a/room/runtime/src/main/java/android/arch/persistence/room/InvalidationTracker.java
+++ b/room/runtime/src/main/java/android/arch/persistence/room/InvalidationTracker.java
@@ -159,6 +159,7 @@
             } finally {
                 database.endTransaction();
             }
+            syncTriggers(database);
             mCleanupStatement = database.compileStatement(CLEANUP_SQL);
             mInitialized = true;
         }
@@ -219,6 +220,7 @@
      *
      * @param observer The observer which listens the database for changes.
      */
+    @WorkerThread
     public void addObserver(@NonNull Observer observer) {
         final String[] tableNames = observer.mTables;
         int[] tableIds = new int[tableNames.length];
@@ -240,7 +242,7 @@
             currentObserver = mObserverMap.putIfAbsent(observer, wrapper);
         }
         if (currentObserver == null && mObservedTableTracker.onAdded(tableIds)) {
-            ArchTaskExecutor.getInstance().executeOnDiskIO(mSyncTriggers);
+            syncTriggers();
         }
     }
 
@@ -265,65 +267,17 @@
      * @param observer The observer to remove.
      */
     @SuppressWarnings("WeakerAccess")
+    @WorkerThread
     public void removeObserver(@NonNull final Observer observer) {
         ObserverWrapper wrapper;
         synchronized (mObserverMap) {
             wrapper = mObserverMap.remove(observer);
         }
         if (wrapper != null && mObservedTableTracker.onRemoved(wrapper.mTableIds)) {
-            ArchTaskExecutor.getInstance().executeOnDiskIO(mSyncTriggers);
+            syncTriggers();
         }
     }
 
-    private Runnable mSyncTriggers = new Runnable() {
-        @Override
-        public void run() {
-            if (mDatabase.inTransaction()) {
-                // we won't run this inside another transaction.
-                return;
-            }
-            if (!ensureInitialization()) {
-                return;
-            }
-            try {
-                // This method runs in a while loop because while changes are synced to db, another
-                // runnable may be skipped. If we cause it to skip, we need to do its work.
-                while (true) {
-                    // there is a potential race condition where another mSyncTriggers runnable
-                    // can start running right after we get the tables list to sync.
-                    final int[] tablesToSync = mObservedTableTracker.getTablesToSync();
-                    if (tablesToSync == null) {
-                        return;
-                    }
-                    final int limit = tablesToSync.length;
-                    final SupportSQLiteDatabase writableDatabase = mDatabase.getOpenHelper()
-                            .getWritableDatabase();
-                    try {
-                        writableDatabase.beginTransaction();
-                        for (int tableId = 0; tableId < limit; tableId++) {
-                            switch (tablesToSync[tableId]) {
-                                case ObservedTableTracker.ADD:
-                                    startTrackingTable(writableDatabase, tableId);
-                                    break;
-                                case ObservedTableTracker.REMOVE:
-                                    stopTrackingTable(writableDatabase, tableId);
-                                    break;
-                            }
-                        }
-                        writableDatabase.setTransactionSuccessful();
-                    } finally {
-                        writableDatabase.endTransaction();
-                    }
-                    mObservedTableTracker.onSyncCompleted();
-                }
-            } catch (IllegalStateException | SQLiteException exception) {
-                // may happen if db is closed. just log.
-                Log.e(Room.LOG_TAG, "Cannot run invalidation tracker. Is the db closed?",
-                        exception);
-            }
-        }
-    };
-
     private boolean ensureInitialization() {
         if (!mDatabase.isOpen()) {
             return false;
@@ -444,6 +398,53 @@
         mRefreshRunnable.run();
     }
 
+    void syncTriggers(SupportSQLiteDatabase database) {
+        if (database.inTransaction()) {
+            // we won't run this inside another transaction.
+            return;
+        }
+        try {
+            // This method runs in a while loop because while changes are synced to db, another
+            // runnable may be skipped. If we cause it to skip, we need to do its work.
+            while (true) {
+                Lock closeLock = mDatabase.getCloseLock();
+                closeLock.lock();
+                try {
+                    // there is a potential race condition where another mSyncTriggers runnable
+                    // can start running right after we get the tables list to sync.
+                    final int[] tablesToSync = mObservedTableTracker.getTablesToSync();
+                    if (tablesToSync == null) {
+                        return;
+                    }
+                    final int limit = tablesToSync.length;
+                    try {
+                        database.beginTransaction();
+                        for (int tableId = 0; tableId < limit; tableId++) {
+                            switch (tablesToSync[tableId]) {
+                                case ObservedTableTracker.ADD:
+                                    startTrackingTable(database, tableId);
+                                    break;
+                                case ObservedTableTracker.REMOVE:
+                                    stopTrackingTable(database, tableId);
+                                    break;
+                            }
+                        }
+                        database.setTransactionSuccessful();
+                    } finally {
+                        database.endTransaction();
+                    }
+                    mObservedTableTracker.onSyncCompleted();
+                } finally {
+                    closeLock.unlock();
+                }
+            }
+        } catch (IllegalStateException | SQLiteException exception) {
+            // may happen if db is closed. just log.
+            Log.e(Room.LOG_TAG, "Cannot run invalidation tracker. Is the db closed?",
+                    exception);
+        }
+    }
+
     /**
      * Called by RoomDatabase before each beginTransaction call.
      * <p>
@@ -453,7 +454,10 @@
      * This api should eventually be public.
      */
     void syncTriggers() {
-        mSyncTriggers.run();
+        if (!mDatabase.isOpen()) {
+            return;
+        }
+        syncTriggers(mDatabase.getOpenHelper().getWritableDatabase());
     }
 
     /**
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/RoomDatabase.java b/room/runtime/src/main/java/android/arch/persistence/room/RoomDatabase.java
index b2a9f0e..90131ed 100644
--- a/room/runtime/src/main/java/android/arch/persistence/room/RoomDatabase.java
+++ b/room/runtime/src/main/java/android/arch/persistence/room/RoomDatabase.java
@@ -243,8 +243,9 @@
      */
     public void beginTransaction() {
         assertNotMainThread();
-        mInvalidationTracker.syncTriggers();
-        mOpenHelper.getWritableDatabase().beginTransaction();
+        SupportSQLiteDatabase database = mOpenHelper.getWritableDatabase();
+        mInvalidationTracker.syncTriggers(database);
+        database.beginTransaction();
     }
 
     /**
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/RoomSQLiteQuery.java b/room/runtime/src/main/java/android/arch/persistence/room/RoomSQLiteQuery.java
index a10cc52..c4ff4bd 100644
--- a/room/runtime/src/main/java/android/arch/persistence/room/RoomSQLiteQuery.java
+++ b/room/runtime/src/main/java/android/arch/persistence/room/RoomSQLiteQuery.java
@@ -79,6 +79,55 @@
     static final TreeMap<Integer, RoomSQLiteQuery> sQueryPool = new TreeMap<>();
 
     /**
+     * Copies the given SupportSQLiteQuery and converts it into RoomSQLiteQuery.
+     *
+     * @param supportSQLiteQuery The query to copy from
+     * @return A new query copied from the provided one.
+     */
+    public static RoomSQLiteQuery copyFrom(SupportSQLiteQuery supportSQLiteQuery) {
+        final RoomSQLiteQuery query = RoomSQLiteQuery.acquire(
+                supportSQLiteQuery.getSql(),
+                supportSQLiteQuery.getArgCount());
+        supportSQLiteQuery.bindTo(new SupportSQLiteProgram() {
+            @Override
+            public void bindNull(int index) {
+                query.bindNull(index);
+            }
+
+            @Override
+            public void bindLong(int index, long value) {
+                query.bindLong(index, value);
+            }
+
+            @Override
+            public void bindDouble(int index, double value) {
+                query.bindDouble(index, value);
+            }
+
+            @Override
+            public void bindString(int index, String value) {
+                query.bindString(index, value);
+            }
+
+            @Override
+            public void bindBlob(int index, byte[] value) {
+                query.bindBlob(index, value);
+            }
+
+            @Override
+            public void clearBindings() {
+                query.clearBindings();
+            }
+
+            @Override
+            public void close() {
+                // ignored.
+            }
+        });
+        return query;
+    }
+
+    /**
      * Returns a new RoomSQLiteQuery that can accept the given number of arguments and holds the
      * given query.
      *
@@ -152,6 +201,7 @@
         return mQuery;
     }
 
+    @Override
     public int getArgCount() {
         return mArgCount;
     }
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/paging/LimitOffsetDataSource.java b/room/runtime/src/main/java/android/arch/persistence/room/paging/LimitOffsetDataSource.java
index baa5b43..73777c4 100644
--- a/room/runtime/src/main/java/android/arch/persistence/room/paging/LimitOffsetDataSource.java
+++ b/room/runtime/src/main/java/android/arch/persistence/room/paging/LimitOffsetDataSource.java
@@ -17,6 +17,7 @@
 package android.arch.persistence.room.paging;
 
 import android.arch.paging.PositionalDataSource;
+import android.arch.persistence.db.SupportSQLiteQuery;
 import android.arch.persistence.room.InvalidationTracker;
 import android.arch.persistence.room.RoomDatabase;
 import android.arch.persistence.room.RoomSQLiteQuery;
@@ -52,6 +53,11 @@
     private final InvalidationTracker.Observer mObserver;
     private final boolean mInTransaction;
 
+    protected LimitOffsetDataSource(RoomDatabase db, SupportSQLiteQuery query,
+            boolean inTransaction, String... tables) {
+        this(db, RoomSQLiteQuery.copyFrom(query), inTransaction, tables);
+    }
+
     protected LimitOffsetDataSource(RoomDatabase db, RoomSQLiteQuery query,
             boolean inTransaction, String... tables) {
         mDb = db;
diff --git a/room/runtime/src/test/java/android/arch/persistence/room/InvalidationTrackerTest.java b/room/runtime/src/test/java/android/arch/persistence/room/InvalidationTrackerTest.java
index d7474fd..ca091d5 100644
--- a/room/runtime/src/test/java/android/arch/persistence/room/InvalidationTrackerTest.java
+++ b/room/runtime/src/test/java/android/arch/persistence/room/InvalidationTrackerTest.java
@@ -125,13 +125,10 @@
     public void addRemoveObserver() throws Exception {
         InvalidationTracker.Observer observer = new LatchObserver(1, "a");
         mTracker.addObserver(observer);
-        drainTasks();
         assertThat(mTracker.mObserverMap.size(), is(1));
         mTracker.removeObserver(new LatchObserver(1, "a"));
-        drainTasks();
         assertThat(mTracker.mObserverMap.size(), is(1));
         mTracker.removeObserver(observer);
-        drainTasks();
         assertThat(mTracker.mObserverMap.size(), is(0));
     }
 
@@ -241,9 +238,9 @@
 
     @Test
     public void closedDb() {
+        doReturn(false).when(mRoomDatabase).isOpen();
         doThrow(new IllegalStateException("foo")).when(mOpenHelper).getWritableDatabase();
         mTracker.addObserver(new LatchObserver(1, "a", "b"));
-        mTracker.syncTriggers();
         mTracker.mRefreshRunnable.run();
     }
 
diff --git a/room/rxjava2/build.gradle b/room/rxjava2/build.gradle
index d3d7bc9..8dcaa6b 100644
--- a/room/rxjava2/build.gradle
+++ b/room/rxjava2/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/room/testing/build.gradle b/room/testing/build.gradle
index d584b54..48ac8a7 100644
--- a/room/testing/build.gradle
+++ b/room/testing/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
-import android.support.SupportLibraryExtension
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/samples/SupportSliceDemos/build.gradle b/samples/SupportSliceDemos/build.gradle
index a0b236d..4eb590e 100644
--- a/samples/SupportSliceDemos/build.gradle
+++ b/samples/SupportSliceDemos/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("SupportAndroidTestAppPlugin")
diff --git a/samples/ViewPager2Demos/OWNERS b/samples/ViewPager2Demos/OWNERS
new file mode 100644
index 0000000..e24ee8b
--- /dev/null
+++ b/samples/ViewPager2Demos/OWNERS
@@ -0,0 +1 @@
+jgielzak@google.com
\ No newline at end of file
diff --git a/samples/ViewPager2Demos/src/main/AndroidManifest.xml b/samples/ViewPager2Demos/src/main/AndroidManifest.xml
index 57e52d3..71d115e 100644
--- a/samples/ViewPager2Demos/src/main/AndroidManifest.xml
+++ b/samples/ViewPager2Demos/src/main/AndroidManifest.xml
@@ -16,7 +16,7 @@
 
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.androidx.widget.viewpager2">
+    package="com.example.androidx.viewpager2">
 
     <application
         android:icon="@drawable/app_sample_code"
@@ -31,6 +31,13 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".CardFragmentActivity" android:label="CardsFragmentDemo">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.SAMPLE_CODE"/>
+            </intent-filter>
+        </activity>
+
         <activity android:name=".BrowseActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/BrowseActivity.java b/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/BrowseActivity.java
similarity index 97%
rename from samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/BrowseActivity.java
rename to samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/BrowseActivity.java
index 6c36994..41e4f99 100644
--- a/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/BrowseActivity.java
+++ b/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/BrowseActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.example.androidx.widget.viewpager2;
+package com.example.androidx.viewpager2;
 
 import android.app.ListActivity;
 import android.content.Intent;
@@ -45,7 +45,7 @@
         super.onCreate(savedInstanceState);
 
         Intent intent = getIntent();
-        String path = intent.getStringExtra("com.example.androidx.widget.viewpager2");
+        String path = intent.getStringExtra("com.example.androidx.viewpager2");
 
         if (path == null) {
             path = "";
@@ -131,7 +131,7 @@
     protected Intent browseIntent(String path) {
         Intent result = new Intent();
         result.setClass(this, BrowseActivity.class);
-        result.putExtra("com.example.androidx.widget.viewpager2", path);
+        result.putExtra("com.example.androidx.viewpager2", path);
         return result;
     }
 
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/CardActivity.java b/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/CardActivity.java
new file mode 100644
index 0000000..7c43127
--- /dev/null
+++ b/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/CardActivity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 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.androidx.viewpager2;
+
+import static java.util.Collections.unmodifiableList;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.widget.RecyclerView;
+import android.view.ViewGroup;
+
+import com.example.androidx.viewpager2.cards.Card;
+import com.example.androidx.viewpager2.cards.CardView;
+
+import java.util.List;
+
+import androidx.viewpager2.widget.ViewPager2;
+
+/**
+ * Shows how to use {@link ViewPager2#setAdapter(RecyclerView.Adapter)}
+ *
+ * @see CardFragmentActivity
+ */
+public class CardActivity extends Activity {
+    private static final List<Card> sCards = unmodifiableList(Card.createDeck52());
+
+    @Override
+    public void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        setContentView(R.layout.activity_card_layout);
+
+        this.<ViewPager2>findViewById(R.id.view_pager).setAdapter(
+                new RecyclerView.Adapter<CardViewHolder>() {
+                    @NonNull
+                    public CardViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
+                            int viewType) {
+                        return new CardViewHolder(new CardView(getLayoutInflater(), parent));
+                    }
+
+                    @Override
+                    public void onBindViewHolder(@NonNull CardViewHolder holder, int position) {
+                        holder.bind(sCards.get(position));
+                    }
+
+                    @Override
+                    public int getItemCount() {
+                        return sCards.size();
+                    }
+                });
+    }
+
+    /** @inheritDoc */
+    public static class CardViewHolder extends RecyclerView.ViewHolder {
+        private final CardView mCardView;
+
+        /** {@inheritDoc} */
+        public CardViewHolder(CardView cardView) {
+            super(cardView.getView());
+            mCardView = cardView;
+        }
+
+        /** @see CardView#bind(Card) */
+        public void bind(Card card) {
+            mCardView.bind(card);
+        }
+    }
+}
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/CardFragmentActivity.java b/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/CardFragmentActivity.java
new file mode 100644
index 0000000..fe4e538
--- /dev/null
+++ b/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/CardFragmentActivity.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.androidx.viewpager2;
+
+import static java.util.Collections.unmodifiableList;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.example.androidx.viewpager2.cards.Card;
+import com.example.androidx.viewpager2.cards.CardView;
+
+import java.util.List;
+
+import androidx.viewpager2.widget.ViewPager2;
+import androidx.viewpager2.widget.ViewPager2.FragmentProvider;
+
+/**
+ * Shows how to use {@link ViewPager2#setAdapter(FragmentManager, FragmentProvider, int)}
+ *
+ * @see CardActivity
+ */
+public class CardFragmentActivity extends FragmentActivity {
+    private static final List<Card> sCards = unmodifiableList(Card.createDeck52());
+
+    @Override
+    public void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        setContentView(R.layout.activity_card_layout);
+
+        this.<ViewPager2>findViewById(R.id.view_pager).setAdapter(getSupportFragmentManager(),
+                new FragmentProvider() {
+                    @Override
+                    public Fragment getItem(int position) {
+                        return CardFragment.create(sCards.get(position));
+                    }
+
+                    @Override
+                    public int getCount() {
+                        return sCards.size();
+                    }
+                },
+                ViewPager2.FragmentRetentionPolicy.SAVE_STATE);
+    }
+
+        /** {@inheritDoc} */
+    public static class CardFragment extends Fragment {
+        @Nullable
+        @Override
+        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+                @Nullable Bundle savedInstanceState) {
+            CardView cardView = new CardView(getLayoutInflater(), container);
+            cardView.bind(Card.fromBundle(getArguments()));
+            return cardView.getView();
+        }
+
+        /** Creates a Fragment for a given {@link Card} */
+        public static CardFragment create(Card card) {
+            CardFragment fragment = new CardFragment();
+            fragment.setArguments(card.toBundle());
+            return fragment;
+        }
+    }
+}
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/cards/Card.java b/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/cards/Card.java
new file mode 100644
index 0000000..28b8fd1
--- /dev/null
+++ b/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/cards/Card.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 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.androidx.viewpager2.cards;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.unmodifiableSet;
+
+import android.os.Bundle;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Playing card
+ */
+public class Card {
+    private static final String ARGS_BUNDLE = Card.class.getName() + ":Bundle";
+
+    private static final Set<Character> SUITS = unmodifiableSet(new LinkedHashSet<>(
+            asList('♣' /* clubs*/, '♦' /* diamonds*/, '♥' /* hearts*/, '♠' /*spades*/)));
+    private static final Set<Character> VALUES = unmodifiableSet(new LinkedHashSet<>(
+            asList('2', '3', '4', '5', '6', '7', '8', '9', 'â’‘', 'J', 'Q', 'K', 'A')));
+
+    private final char mSuit;
+    private final char mValue;
+
+    public Card(char suit, char value) {
+        this.mSuit = checkValidValue(suit, SUITS);
+        this.mValue = checkValidValue(value, VALUES);
+    }
+
+    char getSuit() {
+        return mSuit;
+    }
+
+    String getCornerLabel() {
+        return mValue + "\n" + mSuit;
+    }
+
+    /** Use in conjunction with {@link Card#fromBundle(Bundle)} */
+    public Bundle toBundle() {
+        Bundle args = new Bundle(1);
+        args.putCharArray(ARGS_BUNDLE, new char[]{mSuit, mValue});
+        return args;
+    }
+
+    /** Use in conjunction with {@link Card#toBundle()} */
+    public static Card fromBundle(Bundle bundle) {
+        char[] spec = bundle.getCharArray(ARGS_BUNDLE);
+        return new Card(spec[0], spec[1]);
+    }
+
+    private static char checkValidValue(char value, Set<Character> allowed) {
+        if (allowed.contains(value)) {
+            return value;
+        }
+        throw new IllegalArgumentException("Illegal argument: " + value);
+    }
+
+    /**
+     * Creates a deck of all allowed cards
+     */
+    public static List<Card> createDeck52() {
+        List<Card> result = new ArrayList<>(52);
+        for (Character suit : SUITS) {
+            for (Character value : VALUES) {
+                result.add(new Card(suit, value));
+            }
+        }
+        if (result.size() != 52) {
+            throw new IllegalStateException();
+        }
+        return result;
+    }
+}
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/cards/CardView.java b/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/cards/CardView.java
new file mode 100644
index 0000000..02b52f8
--- /dev/null
+++ b/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/cards/CardView.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.androidx.viewpager2.cards;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.example.androidx.viewpager2.R;
+
+/** Inflates and populates a {@link View} representing a {@link Card} */
+public class CardView {
+    private final View mView;
+    private final TextView mTextSuite;
+    private final TextView mTextCorner1;
+    private final TextView mTextCorner2;
+
+    public CardView(LayoutInflater layoutInflater, ViewGroup container) {
+        mView = layoutInflater.inflate(R.layout.item_card_layout, container, false);
+        mTextSuite = mView.findViewById(R.id.label_center);
+        mTextCorner1 = mView.findViewById(R.id.label_top);
+        mTextCorner2 = mView.findViewById(R.id.label_bottom);
+    }
+
+    /**
+     * Updates the view to represent the passed in card
+     */
+    public void bind(Card card) {
+        mTextSuite.setText(Character.toString(card.getSuit()));
+
+        String cornerLabel = card.getCornerLabel();
+        mTextCorner1.setText(cornerLabel);
+        mTextCorner2.setText(cornerLabel);
+        mTextCorner2.setRotation(180);
+    }
+
+    public View getView() {
+        return mView;
+    }
+}
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/CardActivity.java b/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/CardActivity.java
deleted file mode 100644
index 7484389..0000000
--- a/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/CardActivity.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 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.androidx.widget.viewpager2;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.unmodifiableList;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-import com.example.androidx.widget.viewpager2.cards.Card;
-import com.example.androidx.widget.viewpager2.cards.CardDataAdapter;
-
-import java.util.List;
-
-import androidx.widget.ViewPager2;
-
-/** @inheritDoc */
-public class CardActivity extends Activity {
-    private static final List<Card> sCards = unmodifiableList(asList(
-            new Card('♦', 'A'),
-            new Card('♣', 'K'),
-            new Card('♥', 'J'),
-            new Card('♠', '9'),
-            new Card('♦', '2')));
-
-    @Override
-    public void onCreate(Bundle bundle) {
-        super.onCreate(bundle);
-        setContentView(R.layout.activity_card_layout);
-
-        this.<ViewPager2>findViewById(R.id.view_pager).setAdapter(
-                new CardDataAdapter(getLayoutInflater(), sCards));
-    }
-}
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/cards/Card.java b/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/cards/Card.java
deleted file mode 100644
index 02f3271..0000000
--- a/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/cards/Card.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 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.androidx.widget.viewpager2.cards;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.unmodifiableSet;
-
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-/**
- * Playing card
- */
-public class Card {
-    private static final Set<Character> SUITS = unmodifiableSet(new LinkedHashSet<>(
-            asList('♦', /* diamonds*/ '♣', /*, clubs*/ '♥', /* hearts*/ '♠' /*spades*/)));
-    private static final Set<Character> VALUES = unmodifiableSet(new LinkedHashSet<>(
-            asList('2', '3', '4', '5', '6', '7', '8', '9', 'â’‘', 'J', 'Q', 'K', 'A')));
-
-    private final char mSuit;
-    private final char mValue;
-
-    public Card(char suit, char value) {
-        this.mSuit = checkValidValue(suit, SUITS);
-        this.mValue = checkValidValue(value, VALUES);
-    }
-
-    public char getSuit() {
-        return mSuit;
-    }
-
-    public String getCornerLabel() {
-        return mValue + "\n" + mSuit;
-    }
-
-    private static char checkValidValue(char value, Set<Character> allowed) {
-        if (allowed.contains(value)) {
-            return value;
-        }
-        throw new IllegalArgumentException("Illegal argument: " + value);
-    }
-}
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/cards/CardDataAdapter.java b/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/cards/CardDataAdapter.java
deleted file mode 100644
index 7f69e1b..0000000
--- a/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/cards/CardDataAdapter.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 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.androidx.widget.viewpager2.cards;
-
-import android.support.annotation.NonNull;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import com.example.androidx.widget.viewpager2.R;
-
-import java.util.List;
-
-/** @inheritDoc */
-public class CardDataAdapter extends RecyclerView.Adapter<CardViewHolder> {
-    private final List<Card> mCards;
-    private final LayoutInflater mLayoutInflater;
-
-    public CardDataAdapter(LayoutInflater layoutInflater, List<Card> cards) {
-        mLayoutInflater = layoutInflater;
-        mCards = cards;
-    }
-
-    /**
-     * @inheritDoc
-     */
-    @NonNull
-    public CardViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-        return new CardViewHolder(
-                mLayoutInflater.inflate(R.layout.item_card_layout, parent, false));
-    }
-
-    @Override
-    public void onBindViewHolder(@NonNull CardViewHolder holder, int position) {
-        holder.apply(mCards.get(position));
-    }
-
-    @Override
-    public int getItemCount() {
-        return mCards.size();
-    }
-}
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/cards/CardViewHolder.java b/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/cards/CardViewHolder.java
deleted file mode 100644
index 8fd0477..0000000
--- a/samples/ViewPager2Demos/src/main/java/com/example/androidx/widget/viewpager2/cards/CardViewHolder.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 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.androidx.widget.viewpager2.cards;
-
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.widget.TextView;
-
-import com.example.androidx.widget.viewpager2.R;
-
-/** @inheritDoc */
-public class CardViewHolder extends RecyclerView.ViewHolder {
-    private final TextView mTextSuite;
-    private final TextView mTextCorner1;
-    private final TextView mTextCorner2;
-
-    public CardViewHolder(View itemView) {
-        super(itemView);
-        mTextSuite = itemView.findViewById(R.id.label_center);
-        mTextCorner1 = itemView.findViewById(R.id.label_top);
-        mTextCorner2 = itemView.findViewById(R.id.label_bottom);
-    }
-
-    /**
-     * Updates the view to represent the passed in card
-     */
-    public void apply(Card card) {
-        mTextSuite.setText(Character.toString(card.getSuit()));
-
-        String cornerLabel = card.getCornerLabel();
-        mTextCorner1.setText(cornerLabel);
-        mTextCorner2.setText(cornerLabel);
-        mTextCorner2.setRotation(180);
-    }
-}
diff --git a/samples/ViewPager2Demos/src/main/res/layout/activity_card_layout.xml b/samples/ViewPager2Demos/src/main/res/layout/activity_card_layout.xml
index 3037029..9d996ab 100644
--- a/samples/ViewPager2Demos/src/main/res/layout/activity_card_layout.xml
+++ b/samples/ViewPager2Demos/src/main/res/layout/activity_card_layout.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<androidx.widget.ViewPager2
+<androidx.viewpager2.widget.ViewPager2
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/view_pager"
     android:layout_width="match_parent"
diff --git a/samples/ViewPager2Demos/src/main/res/layout/item_card_layout.xml b/samples/ViewPager2Demos/src/main/res/layout/item_card_layout.xml
index 90a0404..1ae9295 100644
--- a/samples/ViewPager2Demos/src/main/res/layout/item_card_layout.xml
+++ b/samples/ViewPager2Demos/src/main/res/layout/item_card_layout.xml
@@ -26,6 +26,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="top|start"
+        android:gravity="center"
         android:textAppearance="@android:style/TextAppearance.Medium"/>
 
     <TextView
@@ -40,5 +41,6 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="bottom|end"
+        android:gravity="center"
         android:textAppearance="@android:style/TextAppearance.Medium"/>
 </FrameLayout>
diff --git a/settings.gradle b/settings.gradle
index 06b1c32..5e1b188 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -127,7 +127,7 @@
 //
 /////////////////////////////
 
-includeProject(":support-testutils", "testutils")
+includeProject(":internal-testutils", "testutils")
 
 /////////////////////////////
 //
diff --git a/slices/builders/build.gradle b/slices/builders/build.gradle
index 491617a..4b9a250 100644
--- a/slices/builders/build.gradle
+++ b/slices/builders/build.gradle
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryVersions
-import android.support.LibraryGroups
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryVersions
+import androidx.build.LibraryGroups
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/slices/core/build.gradle b/slices/core/build.gradle
index 8087ab4..f1eed8d 100644
--- a/slices/core/build.gradle
+++ b/slices/core/build.gradle
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.*
 
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/slices/view/build.gradle b/slices/view/build.gradle
index 0d9d277..9b7069e 100644
--- a/slices/view/build.gradle
+++ b/slices/view/build.gradle
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryVersions
-import android.support.LibraryGroups
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryVersions
+import androidx.build.LibraryGroups
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/slidingpanelayout/build.gradle b/slidingpanelayout/build.gradle
index d084e63..4e24b3f 100644
--- a/slidingpanelayout/build.gradle
+++ b/slidingpanelayout/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/swiperefreshlayout/build.gradle b/swiperefreshlayout/build.gradle
index 0479bbe..6654e94 100644
--- a/swiperefreshlayout/build.gradle
+++ b/swiperefreshlayout/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
@@ -18,7 +18,7 @@
     androidTestImplementation(ESPRESSO_CONTRIB, libs.exclude_support)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation project(':support-testutils'), {
+    androidTestImplementation project(':internal-testutils'), {
         exclude group: 'com.android.support', module: 'swiperefreshlayout'
     }
 }
diff --git a/swiperefreshlayout/src/androidTest/java/android/support/v4/widget/SwipeRefreshLayoutTest.java b/swiperefreshlayout/src/androidTest/java/android/support/v4/widget/SwipeRefreshLayoutTest.java
index fe53293..96c2dac 100644
--- a/swiperefreshlayout/src/androidTest/java/android/support/v4/widget/SwipeRefreshLayoutTest.java
+++ b/swiperefreshlayout/src/androidTest/java/android/support/v4/widget/SwipeRefreshLayoutTest.java
@@ -39,7 +39,6 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.testutils.PollingCheck;
 import android.view.View;
 
 import org.junit.Before;
@@ -50,6 +49,8 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import androidx.testutils.PollingCheck;
+
 /**
  * Tests SwipeRefreshLayout widget.
  */
diff --git a/testutils/build.gradle b/testutils/build.gradle
index d75b6b7..d32244f 100644
--- a/testutils/build.gradle
+++ b/testutils/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.*
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/testutils/src/main/AndroidManifest.xml b/testutils/src/main/AndroidManifest.xml
index 4d2fd19..2feb440 100644
--- a/testutils/src/main/AndroidManifest.xml
+++ b/testutils/src/main/AndroidManifest.xml
@@ -14,4 +14,4 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<manifest package="android.support.testutils"/>
+<manifest package="androidx.testutils"/>
diff --git a/testutils/src/main/java/android/support/testutils/AppCompatActivityUtils.java b/testutils/src/main/java/androidx/testutils/AppCompatActivityUtils.java
similarity index 98%
rename from testutils/src/main/java/android/support/testutils/AppCompatActivityUtils.java
rename to testutils/src/main/java/androidx/testutils/AppCompatActivityUtils.java
index 49ccc1b..e36a2ec 100644
--- a/testutils/src/main/java/android/support/testutils/AppCompatActivityUtils.java
+++ b/testutils/src/main/java/androidx/testutils/AppCompatActivityUtils.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support.testutils;
+package androidx.testutils;
 
 import static org.junit.Assert.assertTrue;
 
diff --git a/testutils/src/main/java/android/support/testutils/FragmentActivityUtils.java b/testutils/src/main/java/androidx/testutils/FragmentActivityUtils.java
similarity index 98%
rename from testutils/src/main/java/android/support/testutils/FragmentActivityUtils.java
rename to testutils/src/main/java/androidx/testutils/FragmentActivityUtils.java
index 7d12deb..a4b67e7 100644
--- a/testutils/src/main/java/android/support/testutils/FragmentActivityUtils.java
+++ b/testutils/src/main/java/androidx/testutils/FragmentActivityUtils.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.support.testutils;
+package androidx.testutils;
 
 import static org.junit.Assert.assertTrue;
 
diff --git a/testutils/src/main/java/android/support/testutils/PollingCheck.java b/testutils/src/main/java/androidx/testutils/PollingCheck.java
similarity index 98%
rename from testutils/src/main/java/android/support/testutils/PollingCheck.java
rename to testutils/src/main/java/androidx/testutils/PollingCheck.java
index 8e85896..8fd4852 100644
--- a/testutils/src/main/java/android/support/testutils/PollingCheck.java
+++ b/testutils/src/main/java/androidx/testutils/PollingCheck.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.testutils;
+package androidx.testutils;
 
 import org.junit.Assert;
 
diff --git a/testutils/src/main/java/android/support/testutils/RecreatedActivity.java b/testutils/src/main/java/androidx/testutils/RecreatedActivity.java
similarity index 97%
rename from testutils/src/main/java/android/support/testutils/RecreatedActivity.java
rename to testutils/src/main/java/androidx/testutils/RecreatedActivity.java
index aaea3a9..8430dca 100644
--- a/testutils/src/main/java/android/support/testutils/RecreatedActivity.java
+++ b/testutils/src/main/java/androidx/testutils/RecreatedActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.testutils;
+package androidx.testutils;
 
 import android.os.Bundle;
 import android.support.annotation.Nullable;
diff --git a/testutils/src/main/java/android/support/testutils/RecreatedAppCompatActivity.java b/testutils/src/main/java/androidx/testutils/RecreatedAppCompatActivity.java
similarity index 97%
rename from testutils/src/main/java/android/support/testutils/RecreatedAppCompatActivity.java
rename to testutils/src/main/java/androidx/testutils/RecreatedAppCompatActivity.java
index d5645a3..1a48cf8 100644
--- a/testutils/src/main/java/android/support/testutils/RecreatedAppCompatActivity.java
+++ b/testutils/src/main/java/androidx/testutils/RecreatedAppCompatActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.support.testutils;
+package androidx.testutils;
 
 import android.os.Bundle;
 import android.support.annotation.Nullable;
diff --git a/textclassifier/build.gradle b/textclassifier/build.gradle
index b01cbbf..a6e57de 100644
--- a/textclassifier/build.gradle
+++ b/textclassifier/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/transition/build.gradle b/transition/build.gradle
index d8257ab..c413a73 100644
--- a/transition/build.gradle
+++ b/transition/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/transition/proguard-rules.pro b/transition/proguard-rules.pro
index 6cae5e6..dda2c4e 100644
--- a/transition/proguard-rules.pro
+++ b/transition/proguard-rules.pro
@@ -12,10 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# FragmentTransitionSupport is instantiated in support-fragment via reflection.
--keep public class android.support.transition.FragmentTransitionSupport {
-}
-
 # Keep a field in transition that is used to keep a reference to weakly-referenced object
 -keepclassmembers class android.support.transition.ChangeBounds$* extends android.animation.AnimatorListenerAdapter {
   android.support.transition.ChangeBounds$ViewBounds mViewBounds;
diff --git a/tv-provider/api/current.txt b/tv-provider/api/current.txt
index c486190..ee11d8f 100644
--- a/tv-provider/api/current.txt
+++ b/tv-provider/api/current.txt
@@ -77,10 +77,68 @@
     method public static boolean storeChannelLogo(android.content.Context, long, android.graphics.Bitmap);
   }
 
+  public class PreviewChannel {
+    method public static android.support.media.tv.PreviewChannel fromCursor(android.database.Cursor);
+    method public android.content.Intent getAppLinkIntent() throws java.net.URISyntaxException;
+    method public android.net.Uri getAppLinkIntentUri();
+    method public java.lang.CharSequence getDescription();
+    method public java.lang.CharSequence getDisplayName();
+    method public long getId();
+    method public byte[] getInternalProviderDataByteArray();
+    method public java.lang.Long getInternalProviderFlag1();
+    method public java.lang.Long getInternalProviderFlag2();
+    method public java.lang.Long getInternalProviderFlag3();
+    method public java.lang.Long getInternalProviderFlag4();
+    method public java.lang.String getInternalProviderId();
+    method public android.graphics.Bitmap getLogo(android.content.Context);
+    method public java.lang.String getPackageName();
+    method public java.lang.String getType();
+    method public boolean hasAnyUpdatedValues(android.support.media.tv.PreviewChannel);
+    method public boolean isBrowsable();
+  }
+
+  public static final class PreviewChannel.Builder {
+    ctor public PreviewChannel.Builder();
+    ctor public PreviewChannel.Builder(android.support.media.tv.PreviewChannel);
+    method public android.support.media.tv.PreviewChannel build();
+    method public android.support.media.tv.PreviewChannel.Builder setAppLinkIntent(android.content.Intent);
+    method public android.support.media.tv.PreviewChannel.Builder setAppLinkIntentUri(android.net.Uri);
+    method public android.support.media.tv.PreviewChannel.Builder setDescription(java.lang.CharSequence);
+    method public android.support.media.tv.PreviewChannel.Builder setDisplayName(java.lang.CharSequence);
+    method public android.support.media.tv.PreviewChannel.Builder setInternalProviderData(byte[]);
+    method public android.support.media.tv.PreviewChannel.Builder setInternalProviderFlag1(long);
+    method public android.support.media.tv.PreviewChannel.Builder setInternalProviderFlag2(long);
+    method public android.support.media.tv.PreviewChannel.Builder setInternalProviderFlag3(long);
+    method public android.support.media.tv.PreviewChannel.Builder setInternalProviderFlag4(long);
+    method public android.support.media.tv.PreviewChannel.Builder setInternalProviderId(java.lang.String);
+    method public android.support.media.tv.PreviewChannel.Builder setLogo(android.graphics.Bitmap);
+    method public android.support.media.tv.PreviewChannel.Builder setLogo(android.net.Uri);
+  }
+
+  public class PreviewChannelHelper {
+    ctor public PreviewChannelHelper(android.content.Context);
+    ctor public PreviewChannelHelper(android.content.Context, int, int);
+    method public void deletePreviewChannel(long);
+    method public void deletePreviewProgram(long);
+    method protected android.graphics.Bitmap downloadBitmap(android.net.Uri) throws java.io.IOException;
+    method public java.util.List<android.support.media.tv.PreviewChannel> getAllChannels();
+    method public android.support.media.tv.PreviewChannel getPreviewChannel(long);
+    method public android.support.media.tv.PreviewProgram getPreviewProgram(long);
+    method public android.support.media.tv.WatchNextProgram getWatchNextProgram(long);
+    method public long publishChannel(android.support.media.tv.PreviewChannel) throws java.io.IOException;
+    method public long publishDefaultChannel(android.support.media.tv.PreviewChannel) throws java.io.IOException;
+    method public long publishPreviewProgram(android.support.media.tv.PreviewProgram);
+    method public long publishWatchNextProgram(android.support.media.tv.WatchNextProgram);
+    method public void updatePreviewChannel(long, android.support.media.tv.PreviewChannel) throws java.io.IOException;
+    method public void updatePreviewProgram(long, android.support.media.tv.PreviewProgram);
+    method public void updateWatchNextProgram(android.support.media.tv.WatchNextProgram, long);
+  }
+
   public final class PreviewProgram {
     method public static android.support.media.tv.PreviewProgram fromCursor(android.database.Cursor);
     method public long getChannelId();
     method public int getWeight();
+    method public boolean hasAnyUpdatedValues(android.support.media.tv.PreviewProgram);
     method public android.content.ContentValues toContentValues();
   }
 
@@ -529,6 +587,7 @@
     method public static android.support.media.tv.WatchNextProgram fromCursor(android.database.Cursor);
     method public long getLastEngagementTimeUtcMillis();
     method public int getWatchNextType();
+    method public boolean hasAnyUpdatedValues(android.support.media.tv.WatchNextProgram);
     method public android.content.ContentValues toContentValues();
     field public static final int WATCH_NEXT_TYPE_UNKNOWN = -1; // 0xffffffff
   }
diff --git a/tv-provider/build.gradle b/tv-provider/build.gradle
index a8057c0..eb8cbd5 100644
--- a/tv-provider/build.gradle
+++ b/tv-provider/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
@@ -12,6 +12,7 @@
 
     androidTestImplementation(TEST_RUNNER)
     androidTestImplementation(TEST_RULES)
+    androidTestImplementation(MOCKITO_CORE)
 }
 
 supportLibrary {
diff --git a/tv-provider/src/androidTest/java/android/support/media/tv/PreviewChannelHelperTest.java b/tv-provider/src/androidTest/java/android/support/media/tv/PreviewChannelHelperTest.java
new file mode 100644
index 0000000..679e0ee
--- /dev/null
+++ b/tv-provider/src/androidTest/java/android/support/media/tv/PreviewChannelHelperTest.java
@@ -0,0 +1,612 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.media.tv;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.tv.TvContentRating;
+import android.net.Uri;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Test that {@link PreviewChannelHelper} can perform CRUD operations on
+ * {@link PreviewChannel PreviewChannels} and {@link PreviewProgram PreviewPrograms} correctly.
+ * All of the following tests involve the system content provider.
+ */
+@SmallTest
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+@RunWith(JUnit4.class)
+public class PreviewChannelHelperTest {
+
+
+    private Context mContext;
+
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    /**
+     * taken from {@link PreviewProgram}
+     */
+    private static PreviewProgram.Builder createFullyPopulatedPreviewProgram(long channelId) {
+        return new PreviewProgram.Builder()
+                .setTitle("Google")
+                .setInternalProviderId("ID-4321")
+                .setChannelId(channelId)
+                .setWeight(100)
+                .setPreviewVideoUri(Uri.parse("http://example.com/preview-video.mpg"))
+                .setLastPlaybackPositionMillis(0)
+                .setDurationMillis(60 * 1000)
+                .setIntentUri(Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(
+                        Intent.URI_INTENT_SCHEME)))
+                .setTransient(false)
+                .setType(TvContractCompat.PreviewPrograms.TYPE_MOVIE)
+                .setPosterArtAspectRatio(TvContractCompat.PreviewPrograms.ASPECT_RATIO_2_3)
+                .setThumbnailAspectRatio(TvContractCompat.PreviewPrograms.ASPECT_RATIO_16_9)
+                .setLogoUri(Uri.parse("http://example.com/program-logo.mpg"))
+                .setAvailability(TvContractCompat.PreviewPrograms.AVAILABILITY_AVAILABLE)
+                .setStartingPrice("12.99 USD")
+                .setOfferPrice("4.99 USD")
+                .setReleaseDate("1997")
+                .setItemCount(3)
+                .setLive(false)
+                .setInteractionType(TvContractCompat.PreviewPrograms.INTERACTION_TYPE_LIKES)
+                .setInteractionCount(10200)
+                .setAuthor("author_name")
+                .setReviewRatingStyle(TvContractCompat.PreviewPrograms.REVIEW_RATING_STYLE_STARS)
+                .setReviewRating("4.5")
+                .setSearchable(false)
+                .setThumbnailUri(Uri.parse("http://example.com/thumbnail.png"))
+                .setAudioLanguages(new String[]{"eng", "kor"})
+                .setCanonicalGenres(new String[]{TvContractCompat.Programs.Genres.MOVIES})
+                .setContentRatings(new TvContentRating[]{
+                        TvContentRating.createRating("com.android.tv", "US_TV", "US_TV_Y7")})
+                .setDescription("This is a sample program")
+                .setEpisodeNumber("Pilot", 0)
+                .setEpisodeTitle("Hello World")
+                .setLongDescription("This is a longer description than the previous description")
+                .setPosterArtUri(Uri.parse("http://example.com/poster.png"))
+                .setSeasonNumber("The Final Season", 7)
+                .setSeasonTitle("The Final Season")
+                .setVideoHeight(1080)
+                .setVideoWidth(1920)
+                .setInternalProviderFlag1(0x4)
+                .setInternalProviderFlag2(0x3)
+                .setInternalProviderFlag3(0x2)
+                .setInternalProviderFlag4(0x1)
+                .setBrowsable(true)
+                .setContentId("CID-8642");
+    }
+
+    private static void compareProgram(PreviewProgram programA, PreviewProgram programB) {
+        assertTrue(Arrays.equals(programA.getAudioLanguages(), programB.getAudioLanguages()));
+        assertTrue(Arrays.deepEquals(programA.getCanonicalGenres(), programB.getCanonicalGenres()));
+        assertEquals(programA.getChannelId(), programB.getChannelId());
+        assertTrue(Arrays.deepEquals(programA.getContentRatings(), programB.getContentRatings()));
+        assertEquals(programA.getDescription(), programB.getDescription());
+        assertEquals(programA.getEpisodeNumber(), programB.getEpisodeNumber());
+        assertEquals(programA.getEpisodeTitle(), programB.getEpisodeTitle());
+        assertEquals(programA.getLongDescription(), programB.getLongDescription());
+        assertEquals(programA.getPosterArtUri(), programB.getPosterArtUri());
+        assertEquals(programA.getSeasonNumber(), programB.getSeasonNumber());
+        assertEquals(programA.getThumbnailUri(), programB.getThumbnailUri());
+        assertEquals(programA.getTitle(), programB.getTitle());
+        assertEquals(programA.getVideoHeight(), programB.getVideoHeight());
+        assertEquals(programA.getVideoWidth(), programB.getVideoWidth());
+        assertEquals(programA.isSearchable(), programB.isSearchable());
+        assertEquals(programA.getInternalProviderFlag1(), programB.getInternalProviderFlag1());
+        assertEquals(programA.getInternalProviderFlag2(), programB.getInternalProviderFlag2());
+        assertEquals(programA.getInternalProviderFlag3(), programB.getInternalProviderFlag3());
+        assertEquals(programA.getInternalProviderFlag4(), programB.getInternalProviderFlag4());
+        assertTrue(Objects.equals(programA.getSeasonTitle(), programB.getSeasonTitle()));
+        assertEquals(programA.getInternalProviderId(), programB.getInternalProviderId());
+        assertEquals(programA.getPreviewVideoUri(), programB.getPreviewVideoUri());
+        assertEquals(programA.getLastPlaybackPositionMillis(),
+                programB.getLastPlaybackPositionMillis());
+        assertEquals(programA.getDurationMillis(), programB.getDurationMillis());
+        assertEquals(programA.getIntentUri(), programB.getIntentUri());
+        assertEquals(programA.getWeight(), programB.getWeight());
+        assertEquals(programA.isTransient(), programB.isTransient());
+        assertEquals(programA.getType(), programB.getType());
+        assertEquals(programA.getPosterArtAspectRatio(), programB.getPosterArtAspectRatio());
+        assertEquals(programA.getThumbnailAspectRatio(), programB.getThumbnailAspectRatio());
+        assertEquals(programA.getLogoUri(), programB.getLogoUri());
+        assertEquals(programA.getAvailability(), programB.getAvailability());
+        assertEquals(programA.getStartingPrice(), programB.getStartingPrice());
+        assertEquals(programA.getOfferPrice(), programB.getOfferPrice());
+        assertEquals(programA.getReleaseDate(), programB.getReleaseDate());
+        assertEquals(programA.getItemCount(), programB.getItemCount());
+        assertEquals(programA.isLive(), programB.isLive());
+        assertEquals(programA.getInteractionType(), programB.getInteractionType());
+        assertEquals(programA.getInteractionCount(), programB.getInteractionCount());
+        assertEquals(programA.getAuthor(), programB.getAuthor());
+        assertEquals(programA.getReviewRatingStyle(), programB.getReviewRatingStyle());
+        assertEquals(programA.getReviewRating(), programB.getReviewRating());
+        assertEquals(programA.getContentId(), programB.getContentId());
+    }
+
+    private static WatchNextProgram.Builder createFullyPopulatedWatchNextProgram() {
+        return new WatchNextProgram.Builder()
+                .setTitle("Google")
+                .setInternalProviderId("ID-4321")
+                .setPreviewVideoUri(Uri.parse("http://example.com/preview-video.mpg"))
+                .setLastPlaybackPositionMillis(0)
+                .setDurationMillis(60 * 1000)
+                .setIntentUri(Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(
+                        Intent.URI_INTENT_SCHEME)))
+                .setTransient(false)
+                .setType(TvContractCompat.WatchNextPrograms.TYPE_MOVIE)
+                .setWatchNextType(TvContractCompat.WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE)
+                .setPosterArtAspectRatio(TvContractCompat.WatchNextPrograms.ASPECT_RATIO_2_3)
+                .setThumbnailAspectRatio(TvContractCompat.WatchNextPrograms.ASPECT_RATIO_16_9)
+                .setLogoUri(Uri.parse("http://example.com/program-logo.mpg"))
+                .setAvailability(TvContractCompat.WatchNextPrograms.AVAILABILITY_AVAILABLE)
+                .setStartingPrice("12.99 USD")
+                .setOfferPrice("4.99 USD")
+                .setReleaseDate("1997")
+                .setItemCount(3)
+                .setLive(false)
+                .setInteractionType(TvContractCompat.WatchNextPrograms.INTERACTION_TYPE_LIKES)
+                .setInteractionCount(10200)
+                .setAuthor("author_name")
+                .setReviewRatingStyle(TvContractCompat.WatchNextPrograms.REVIEW_RATING_STYLE_STARS)
+                .setReviewRating("4.5")
+                .setSearchable(false)
+                .setThumbnailUri(Uri.parse("http://example.com/thumbnail.png"))
+                .setAudioLanguages(new String[]{"eng", "kor"})
+                .setCanonicalGenres(new String[]{TvContractCompat.Programs.Genres.MOVIES})
+                .setContentRatings(new TvContentRating[]{
+                        TvContentRating.createRating("com.android.tv", "US_TV", "US_TV_Y7")})
+                .setDescription("This is a sample program")
+                .setEpisodeNumber("Pilot", 0)
+                .setEpisodeTitle("Hello World")
+                .setLongDescription("This is a longer description than the previous description")
+                .setPosterArtUri(Uri.parse("http://example.com/poster.png"))
+                .setSeasonNumber("The Final Season", 7)
+                .setSeasonTitle("The Final Season")
+                .setVideoHeight(1080)
+                .setVideoWidth(1920)
+                .setInternalProviderFlag1(0x4)
+                .setInternalProviderFlag2(0x3)
+                .setInternalProviderFlag3(0x2)
+                .setInternalProviderFlag4(0x1)
+                .setBrowsable(true)
+                .setContentId("CID-8442");
+    }
+
+    private static void compareProgram(WatchNextProgram programA, WatchNextProgram programB) {
+        assertTrue(Arrays.equals(programA.getAudioLanguages(), programB.getAudioLanguages()));
+        assertTrue(Arrays.deepEquals(programA.getCanonicalGenres(), programB.getCanonicalGenres()));
+        assertTrue(Arrays.deepEquals(programA.getContentRatings(), programB.getContentRatings()));
+        assertEquals(programA.getDescription(), programB.getDescription());
+        assertEquals(programA.getEpisodeNumber(), programB.getEpisodeNumber());
+        assertEquals(programA.getEpisodeTitle(), programB.getEpisodeTitle());
+        assertEquals(programA.getLongDescription(), programB.getLongDescription());
+        assertEquals(programA.getPosterArtUri(), programB.getPosterArtUri());
+        assertEquals(programA.getSeasonNumber(), programB.getSeasonNumber());
+        assertEquals(programA.getThumbnailUri(), programB.getThumbnailUri());
+        assertEquals(programA.getTitle(), programB.getTitle());
+        assertEquals(programA.getVideoHeight(), programB.getVideoHeight());
+        assertEquals(programA.getVideoWidth(), programB.getVideoWidth());
+        assertEquals(programA.isSearchable(), programB.isSearchable());
+        assertEquals(programA.getInternalProviderFlag1(), programB.getInternalProviderFlag1());
+        assertEquals(programA.getInternalProviderFlag2(), programB.getInternalProviderFlag2());
+        assertEquals(programA.getInternalProviderFlag3(), programB.getInternalProviderFlag3());
+        assertEquals(programA.getInternalProviderFlag4(), programB.getInternalProviderFlag4());
+        assertTrue(Objects.equals(programA.getSeasonTitle(), programB.getSeasonTitle()));
+        assertEquals(programA.getInternalProviderId(), programB.getInternalProviderId());
+        assertEquals(programA.getPreviewVideoUri(), programB.getPreviewVideoUri());
+        assertEquals(programA.getLastPlaybackPositionMillis(),
+                programB.getLastPlaybackPositionMillis());
+        assertEquals(programA.getDurationMillis(), programB.getDurationMillis());
+        assertEquals(programA.getIntentUri(), programB.getIntentUri());
+        assertEquals(programA.isTransient(), programB.isTransient());
+        assertEquals(programA.getType(), programB.getType());
+        assertEquals(programA.getWatchNextType(), programB.getWatchNextType());
+        assertEquals(programA.getPosterArtAspectRatio(), programB.getPosterArtAspectRatio());
+        assertEquals(programA.getThumbnailAspectRatio(), programB.getThumbnailAspectRatio());
+        assertEquals(programA.getLogoUri(), programB.getLogoUri());
+        assertEquals(programA.getAvailability(), programB.getAvailability());
+        assertEquals(programA.getStartingPrice(), programB.getStartingPrice());
+        assertEquals(programA.getOfferPrice(), programB.getOfferPrice());
+        assertEquals(programA.getReleaseDate(), programB.getReleaseDate());
+        assertEquals(programA.getItemCount(), programB.getItemCount());
+        assertEquals(programA.isLive(), programB.isLive());
+        assertEquals(programA.getInteractionType(), programB.getInteractionType());
+        assertEquals(programA.getInteractionCount(), programB.getInteractionCount());
+        assertEquals(programA.getAuthor(), programB.getAuthor());
+        assertEquals(programA.getReviewRatingStyle(), programB.getReviewRatingStyle());
+        assertEquals(programA.getReviewRating(), programB.getReviewRating());
+        assertEquals(programA.getContentId(), programB.getContentId());
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+
+    }
+
+    @After
+    public void tearDown() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        mContext.getContentResolver().delete(
+                TvContractCompat.Channels.CONTENT_URI, null, null);
+        mContext = null;
+    }
+
+    /**
+     * Test CR of CRUD
+     * Test that the PreviewChannelHelper can correctly create and read preview channels.
+     */
+    @Test
+    public void testPreviewChannelCreation() throws IOException {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
+        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
+        long channelId = helper.publishDefaultChannel(builder.build());
+        PreviewChannel channelFromTvProvider = getPreviewChannel(helper, channelId);
+        assertTrue(channelsEqual(builder.build(), channelFromTvProvider));
+    }
+
+    @Test
+    public void testLogoRequiredForChannelCreation() throws IOException {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
+        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
+        builder.setLogo(Uri.parse("bogus"));
+        thrown.expect(IOException.class);
+        helper.publishDefaultChannel(builder.build());
+        List<PreviewChannel> channels = helper.getAllChannels();
+        assertEquals(0, channels.size());
+    }
+
+    /**
+     * Test CR of CRUD
+     * Test that the PreviewChannelHelper can correctly create and read preview channels, when
+     * internalProviderId is null.
+     */
+    @Test
+    public void testPreviewChannelCreationWithNullProviderId() throws IOException {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
+        builder.setInternalProviderId(null);
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
+        long channelId = helper.publishChannel(builder.build());
+        PreviewChannel channelFromTvProvider = getPreviewChannel(helper, channelId);
+        assertTrue(channelsEqual(builder.build(), channelFromTvProvider));
+    }
+
+    /**
+     * All this method is actually doing is
+     * <pre>
+     *
+     *     PreviewChannel channelFromTvProvider = helper.getPreviewChannel(channelId);
+     * </pre>
+     * However, due to a known issue, when logo is persisted, the file status is not consistent
+     * between openInputStream and openOutputStream. So as a workaround, a wait period is applied
+     * to make sure that the logo file is written into the disk.
+     */
+    private PreviewChannel getPreviewChannel(PreviewChannelHelper helper,
+            long channelId) {
+        boolean logoReady = false;
+        PreviewChannel channel = null;
+        while (!logoReady) {
+            try {
+                Thread.sleep(50);
+            } catch (InterruptedException e) {
+            }
+            channel = helper.getPreviewChannel(channelId);
+            logoReady = null != channel.getLogo(mContext);
+        }
+        return channel;
+    }
+
+    /**
+     * Test CR of CRUD
+     * Test that all published preview channels can be read at once.
+     */
+    @Test
+    public void testAllPublishedChannelsRead() throws IOException {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
+        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
+        builder.setInternalProviderId("1");
+        helper.publishChannel(builder.build());
+        builder.setInternalProviderId("11");
+        helper.publishChannel(builder.build());
+        builder.setInternalProviderId("111");
+        helper.publishChannel(builder.build());
+        builder.setInternalProviderId("1111");
+        helper.publishChannel(builder.build());
+        List<PreviewChannel> allChannels = helper.getAllChannels();
+        assertEquals(4, allChannels.size());
+    }
+
+    /**
+     * Test UR of CRUD
+     * Test that the PreviewChannelHelper can correctly update and read preview channels.
+     */
+    @Test
+    public void testPreviewChannelUpdate() throws IOException {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
+        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
+        long channelId = helper.publishChannel(builder.build());
+        PreviewChannel channelFromTvProvider = getPreviewChannel(helper, channelId);
+        PreviewChannel channel = builder.build();
+        assertTrue(channelsEqual(channel, channelFromTvProvider));
+
+        PreviewChannel patch = new PreviewChannel.Builder()
+                .setDisplayName(channel.getDisplayName())
+                .setAppLinkIntentUri(channel.getAppLinkIntentUri())
+                .setDescription("Patch description").build();
+        helper.updatePreviewChannel(channelId, patch);
+        channelFromTvProvider = helper.getPreviewChannel(channelId);
+        assertFalse(channelsEqual(channel, channelFromTvProvider));
+        assertEquals(channelFromTvProvider.getDescription(), "Patch description");
+    }
+
+    /**
+     * Tests that data is not being updated unnecessarily
+     */
+    @Test
+    public void testDefensiveUpdatePreviewChannel() throws IOException {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        final int[] channelUpdateCount = {0};
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext) {
+            @Override
+            protected void updatePreviewChannelInternal(long channelId, PreviewChannel channel) {
+                channelUpdateCount[0]++;
+            }
+        };
+        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
+        long channelId = helper.publishChannel(builder.build());
+        PreviewChannel fromProvider = helper.getPreviewChannel(channelId);
+        channelsEqual(builder.build(), fromProvider);
+        helper.updatePreviewChannel(channelId, builder.build());
+        assertEquals(0, channelUpdateCount[0]);
+
+        final Uri uri = Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(Intent.URI_INTENT_SCHEME));
+        PreviewChannel channel = new PreviewChannel.Builder()
+                .setDisplayName("Test Display Name Udpate")
+                .setDescription("Test Preview Channel Description")
+                .setAppLinkIntentUri(uri).build();
+
+        helper.updatePreviewChannel(channelId, channel);
+        assertEquals(1, channelUpdateCount[0]);
+    }
+
+    @Test
+    public void testPreviewResolverChannelDeletion() throws IOException {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
+        PreviewChannel.Builder builder = createFullyPopulatedPreviewChannel();
+        long channelId = helper.publishChannel(builder.build());
+        PreviewChannel channelFromTvProvider = getPreviewChannel(helper, channelId);
+        assertTrue(channelsEqual(builder.build(), channelFromTvProvider));
+
+        helper.deletePreviewChannel(channelId);
+        channelFromTvProvider = helper.getPreviewChannel(channelId);
+        assertNull(channelFromTvProvider);
+    }
+
+    @Test
+    public void testPreviewProgramCreation() throws IOException {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
+        PreviewChannel.Builder channelBuilder = createFullyPopulatedPreviewChannel();
+        long channelId = helper.publishChannel(channelBuilder.build());
+        PreviewProgram program = createFullyPopulatedPreviewProgram(channelId).build();
+        long programId = helper.publishPreviewProgram(program);
+        PreviewProgram programFromProvider = helper.getPreviewProgram(programId);
+        compareProgram(program, programFromProvider);
+    }
+
+    @Test
+    public void testPreviewProgramUpdate() throws IOException {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
+        PreviewChannel.Builder channelBuilder = createFullyPopulatedPreviewChannel();
+        long channelId = helper.publishChannel(channelBuilder.build());
+        PreviewProgram.Builder programBuilder = createFullyPopulatedPreviewProgram(channelId);
+        long programId = helper.publishPreviewProgram(programBuilder.build());
+
+        programBuilder.setReleaseDate("2000");
+
+        helper.updatePreviewProgram(programId, programBuilder.build());
+        PreviewProgram programFromProvider = helper.getPreviewProgram(programId);
+        compareProgram(programBuilder.build(), programFromProvider);
+    }
+
+    /**
+     * Tests that data is not being updated unnecessarily
+     */
+    @Test
+    public void testDefensivePreviewProgramUpdateRequests() throws IOException {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        final int[] programUpdateCount = {0};
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext) {
+
+            @Override
+            public void updatePreviewProgramInternal(long programId, PreviewProgram upgrade) {
+                programUpdateCount[0]++;
+            }
+        };
+        PreviewChannel.Builder channelBuilder = createFullyPopulatedPreviewChannel();
+        long channelId = helper.publishChannel(channelBuilder.build());
+        PreviewProgram.Builder programBuilder = createFullyPopulatedPreviewProgram(channelId);
+        long programId = helper.publishPreviewProgram(programBuilder.build());
+        PreviewProgram programFromProvider = helper.getPreviewProgram(programId);
+        compareProgram(programBuilder.build(), programFromProvider);
+        helper.updatePreviewProgram(programId, programBuilder.build());
+        assertEquals(0, programUpdateCount[0]);
+        programBuilder.setDurationMillis(61 * 1000);
+        helper.updatePreviewProgram(programId, programBuilder.build());
+        assertEquals(1, programUpdateCount[0]);
+    }
+
+    @Test
+    public void testDeletePreviewProgram() throws IOException {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
+        PreviewChannel.Builder channelBuilder = createFullyPopulatedPreviewChannel();
+        long channelId = helper.publishChannel(channelBuilder.build());
+        PreviewProgram.Builder programBuilder = createFullyPopulatedPreviewProgram(channelId);
+        long programId = helper.publishPreviewProgram(programBuilder.build());
+
+        helper.deletePreviewProgram(programId);
+        PreviewProgram programFromProvider = helper.getPreviewProgram(programId);
+        assertNull(programFromProvider);
+    }
+
+    @Test
+    public void testWatchNextProgramCreation() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
+        WatchNextProgram program = createFullyPopulatedWatchNextProgram().build();
+        long programId = helper.publishWatchNextProgram(program);
+        WatchNextProgram programFromProvider = helper.getWatchNextProgram(programId);
+        compareProgram(program, programFromProvider);
+    }
+
+    @Test
+    public void testUpdateWatchNextProgram() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext);
+        WatchNextProgram.Builder builder = createFullyPopulatedWatchNextProgram();
+        long programId = helper.publishWatchNextProgram(builder.build());
+        builder.setOfferPrice("10.99 USD");
+        helper.updateWatchNextProgram(builder.build(), programId);
+
+        WatchNextProgram fromProvider = helper.getWatchNextProgram(programId);
+        compareProgram(builder.build(), fromProvider);
+    }
+
+    /**
+     * Tests that data is not being updated unnecessarily
+     */
+    @Test
+    public void testDefensiveUpdateWatchNextProgram() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        final int[] programUpdateCount = {0};
+        PreviewChannelHelper helper = new PreviewChannelHelper(mContext) {
+            @Override
+            protected void updateWatchNextProgram(long programId, WatchNextProgram upgrade) {
+                programUpdateCount[0]++;
+            }
+        };
+        WatchNextProgram.Builder builder = createFullyPopulatedWatchNextProgram();
+        long programId = helper.publishWatchNextProgram(builder.build());
+        WatchNextProgram fromProvider = helper.getWatchNextProgram(programId);
+        compareProgram(builder.build(), fromProvider);
+        helper.updateWatchNextProgram(builder.build(), programId);
+        assertEquals(0, programUpdateCount[0]);
+        builder.setReleaseDate("2000");
+        helper.updateWatchNextProgram(builder.build(), programId);
+        assertEquals(1, programUpdateCount[0]);
+    }
+
+    private boolean channelsEqual(PreviewChannel channelA, PreviewChannel channelB) {
+        boolean result = channelA.getDisplayName().equals(channelB.getDisplayName())
+                && channelA.getType().equals(channelB.getType())
+                && channelA.getAppLinkIntentUri().equals(channelB.getAppLinkIntentUri())
+                && channelA.getDescription().equals(channelB.getDescription())
+                && channelA.getPackageName().equals(channelB.getPackageName())
+                && channelA.getInternalProviderFlag1() == channelB.getInternalProviderFlag1()
+                && channelA.getInternalProviderFlag2() == channelB.getInternalProviderFlag2()
+                && channelA.getInternalProviderFlag3() == channelB.getInternalProviderFlag3()
+                && channelA.getInternalProviderFlag4() == channelB.getInternalProviderFlag4()
+                && (channelA.getInternalProviderId() == null
+                && channelB.getInternalProviderId() == null
+                || channelA.getInternalProviderId().equals(channelB.getInternalProviderId()))
+                && (null != channelA.getLogo(mContext) && null != channelB.getLogo(
+                mContext))
+                && Arrays.equals(channelA.getInternalProviderDataByteArray(),
+                channelB.getInternalProviderDataByteArray());
+        return result;
+    }
+
+    public PreviewChannel.Builder createFullyPopulatedPreviewChannel() {
+        Bitmap logo = BitmapFactory.decodeResource(mContext.getResources(),
+                android.support.media.tv.test.R.drawable.test_icon);
+        assertNotNull(logo);
+        return new PreviewChannel.Builder()
+                .setAppLinkIntent(new Intent())
+                .setDescription("Test Preview Channel Description")
+                .setDisplayName("Test Display Name")
+                .setPackageName("android.support.media.tv.test")
+                .setInternalProviderFlag1(0x1)
+                .setInternalProviderFlag2(0x2)
+                .setInternalProviderFlag3(0x3)
+                .setInternalProviderFlag4(0x4)
+                .setInternalProviderId("Test Internal provider id")
+                .setInternalProviderData("Test byte array".getBytes())
+                .setLogo(logo);
+    }
+}
diff --git a/tv-provider/src/androidTest/java/android/support/media/tv/PreviewChannelTest.java b/tv-provider/src/androidTest/java/android/support/media/tv/PreviewChannelTest.java
new file mode 100644
index 0000000..ef5e22e
--- /dev/null
+++ b/tv-provider/src/androidTest/java/android/support/media/tv/PreviewChannelTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.media.tv;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Build;
+import android.support.media.tv.TvContractCompat.Channels;
+import android.support.media.tv.test.R;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+
+/**
+ * Tests that PreviewChannels can be created correctly. Additional test, the ones involving the
+ * System Content Provider, are inside {@link PreviewChannelHelperTest}
+ */
+@SmallTest
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+@RunWith(JUnit4.class)
+public class PreviewChannelTest extends TestCase {
+
+    private static final String TAG = "PreviewChannelTest";
+    private Context mContext;
+
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        mContext = InstrumentationRegistry.getContext();
+    }
+
+    @After
+    public void tearDown() {
+        if (!Utils.hasTvInputFramework(InstrumentationRegistry.getContext())) {
+            return;
+        }
+        ContentResolver resolver = mContext.getContentResolver();
+        resolver.delete(Channels.CONTENT_URI, null, null);
+        mContext = null;
+    }
+
+    @Test
+    public void testEmptyPreviewChannel() throws Exception {
+        PreviewChannel.Builder builder = new PreviewChannel.Builder();
+        thrown.expect(IllegalStateException.class);
+        thrown.expectMessage("Need channel name. "
+                + "Use method setDisplayName(String) to set it.");
+        PreviewChannel emptyChannel = builder.build();
+    }
+
+    @Test
+    public void testPartiallyPopulatedPreviewChannel() {
+        final String displayName = "Google";
+        final String description = "This is a test preview channel";
+        final Uri uri = Uri.parse(new Intent(Intent.ACTION_VIEW).toUri(Intent.URI_INTENT_SCHEME));
+
+        PreviewChannel channel = new PreviewChannel.Builder()
+                .setDisplayName(displayName)
+                .setDescription(description)
+                .setAppLinkIntentUri(uri)
+                .setLogo(createLogo()).build();
+
+        assertEquals(displayName, channel.getDisplayName());
+        assertEquals(description, channel.getDescription());
+        assertEquals(uri, channel.getAppLinkIntentUri());
+        assertNotNull(channel.getLogo(mContext));
+        assertNull(channel.getPackageName());
+        assertNull(channel.getInternalProviderDataByteArray());
+        assertNull(channel.getInternalProviderFlag1());
+        assertNull(channel.getInternalProviderFlag2());
+        assertNull(channel.getInternalProviderFlag3());
+        assertNull(channel.getInternalProviderFlag4());
+        assertNull(channel.getInternalProviderId());
+        assertFalse(channel.isBrowsable());
+    }
+
+    @Test
+    public void testFullyPopulatedPreviewChannel() {
+        //test cloning and database I/O
+        PreviewChannel channel = createFullyPopulatedPreviewChannel();
+        PreviewChannel clonedChannelFromCursor = PreviewChannel.fromCursor(
+                getPreviewChannelCursor(channel.toContentValues()));
+        assertTrue(channelsEqual(channel, clonedChannelFromCursor));
+
+        PreviewChannel clonedChannelFromBuilder = new PreviewChannel.Builder(channel).build();
+        assertTrue(channelsEqual(channel, clonedChannelFromBuilder));
+    }
+
+    @Test
+    public void testChannelEquals() {
+        assertEquals(createFullyPopulatedPreviewChannel(), createFullyPopulatedPreviewChannel());
+    }
+
+    private boolean channelsEqual(PreviewChannel channelA, PreviewChannel channelB) {
+        boolean result = channelA.getDisplayName().equals(channelB.getDisplayName())
+                && channelA.getType().equals(channelB.getType())
+                && channelA.getAppLinkIntentUri().equals(channelB.getAppLinkIntentUri())
+                && channelA.getDescription().equals(channelB.getDescription())
+                && channelA.getPackageName().equals(channelB.getPackageName())
+                && channelA.getInternalProviderFlag1() == channelB.getInternalProviderFlag1()
+                && channelA.getInternalProviderFlag2() == channelB.getInternalProviderFlag2()
+                && channelA.getInternalProviderFlag3() == channelB.getInternalProviderFlag3()
+                && channelA.getInternalProviderFlag4() == channelB.getInternalProviderFlag4()
+                && channelA.getInternalProviderId().equals(channelB.getInternalProviderId())
+                && Arrays.equals(channelA.getInternalProviderDataByteArray(),
+                channelB.getInternalProviderDataByteArray());
+        return result;
+    }
+
+    private PreviewChannel createFullyPopulatedPreviewChannel() {
+        return new PreviewChannel.Builder()
+                .setAppLinkIntent(new Intent())
+                .setDescription("Test Preview Channel Description")
+                .setDisplayName("Test Display Name")
+                .setPackageName("android.support.media.tv.test")
+                .setInternalProviderFlag1(0x1)
+                .setInternalProviderFlag2(0x2)
+                .setInternalProviderFlag3(0x3)
+                .setInternalProviderFlag4(0x4)
+                .setInternalProviderId("Test Internal provider id")
+                .setLogo(createLogo()).build();
+    }
+
+    private Bitmap createLogo() {
+        Bitmap logo = BitmapFactory.decodeResource(mContext.getResources(),
+                R.drawable.test_icon);
+        assertNotNull(logo);
+        return logo;
+    }
+
+    private MatrixCursor getPreviewChannelCursor(ContentValues contentValues) {
+        MatrixCursor cursor = new MatrixCursor(PreviewChannel.Columns.PROJECTION);
+        MatrixCursor.RowBuilder rowBuilder = cursor.newRow();
+        for (String col : PreviewChannel.Columns.PROJECTION) {
+            rowBuilder.add(col, contentValues.get(col));
+        }
+        cursor.moveToFirst();
+        return cursor;
+    }
+}
diff --git a/tv-provider/src/main/java/android/support/media/tv/PreviewChannel.java b/tv-provider/src/main/java/android/support/media/tv/PreviewChannel.java
new file mode 100644
index 0000000..79a65f9
--- /dev/null
+++ b/tv-provider/src/main/java/android/support/media/tv/PreviewChannel.java
@@ -0,0 +1,565 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.media.tv;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.annotation.TargetApi;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.WorkerThread;
+import android.support.media.tv.TvContractCompat.Channels;
+import android.support.media.tv.TvContractCompat.Channels.Type;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.FileNotFoundException;
+import java.net.URISyntaxException;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Since API 26, all TV apps may create preview channels and publish them to the home screen.
+ * We call these App Channels (as distinct from the Live Channels row on the home screen). To help
+ * you create App Channels, the support library provides a number of classes prefixed by the word
+ * Preview-.
+ *
+ * This is a convenience class for mapping your app's content into a
+ * {@link TvContractCompat TvProvider Channel} for publication. Use the provided {@link Builder}
+ * for creating your preview channel object. Once you create a preview channel, you can
+ * use {@link PreviewChannelHelper} to publish it and add {@link PreviewProgram programs} to it.
+ */
+@TargetApi(26)
+public class PreviewChannel {
+
+    private static final String TAG = "PreviewChannel";
+    private static final long INVALID_CHANNEL_ID = -1;
+    private static final int IS_BROWSABLE = 1;
+
+    private ContentValues mValues;
+    private volatile Bitmap mLogoImage;
+
+    private Uri mLogoUri;
+    private boolean mLogoChanged;
+
+    /**
+     * Logo is fetched when it is explicitly asked for. mLogoFetched prevents repeated calls in
+     * case there is no logo in fact.
+     */
+    private volatile boolean mLogoFetched;
+
+    private PreviewChannel(Builder builder) {
+        mValues = builder.mValues;
+        mLogoImage = builder.mLogoBitmap;
+        mLogoUri = builder.mLogoUri;
+        mLogoChanged = (mLogoImage != null || mLogoUri != null);
+    }
+
+    /**
+     * Used by {@link PreviewChannelHelper} to transduce a TvProvider channel row into a
+     * PreviewChannel Java object. You never need to use this method unless you want to convert
+     * database rows to PreviewChannel objects yourself.
+     * <p/>
+     * This method assumes the cursor was obtained using {@link android.support.media.tv
+     * .PreviewChannel.Columns#PROJECTION}. This way, all indices are known
+     * beforehand.
+     *
+     * @param cursor a cursor row from the TvProvider
+     * @return a PreviewChannel whose values come from the cursor row
+     */
+    public static PreviewChannel fromCursor(Cursor cursor) {
+        Builder builder = new Builder();
+        builder.setId(cursor.getInt(Columns.COL_ID));
+        builder.setPackageName(cursor.getString(Columns.COL_PACKAGE_NAME));
+        builder.setType(cursor.getString(Columns.COL_TYPE));
+        builder.setDisplayName(cursor.getString(Columns.COL_DISPLAY_NAME));
+        builder.setDescription(cursor.getString(Columns.COL_DESCRIPTION));
+        builder.setAppLinkIntentUri(Uri.parse(cursor.getString(Columns.COL_APP_LINK_INTENT_URI)));
+        builder.setInternalProviderId(cursor.getString(Columns.COL_INTERNAL_PROVIDER_ID));
+        builder.setInternalProviderData(cursor.getBlob(Columns.COL_INTERNAL_PROVIDER_DATA));
+        builder.setInternalProviderFlag1(cursor.getLong(Columns.COL_INTERNAL_PROVIDER_FLAG1));
+        builder.setInternalProviderFlag2(cursor.getLong(Columns.COL_INTERNAL_PROVIDER_FLAG2));
+        builder.setInternalProviderFlag3(cursor.getLong(Columns.COL_INTERNAL_PROVIDER_FLAG3));
+        builder.setInternalProviderFlag4(cursor.getLong(Columns.COL_INTERNAL_PROVIDER_FLAG4));
+        return builder.build();
+    }
+
+    /**
+     * @return the ID the system assigns to this preview channel upon publication.
+     */
+    public long getId() {
+        Long l = mValues.getAsLong(Channels._ID);
+        return l == null ? INVALID_CHANNEL_ID : l;
+    }
+
+    /**
+     * @return package name of the app that created this channel
+     */
+    public String getPackageName() {
+        return mValues.getAsString(Channels.COLUMN_PACKAGE_NAME);
+    }
+
+    /**
+     * @return what type of channel this is. For preview channels, the type is always
+     * TvContractCompat.Channels.TYPE_PREVIEW
+     */
+    @Type
+    public String getType() {
+        return mValues.getAsString(Channels.COLUMN_TYPE);
+    }
+
+    /**
+     * @return The name users see when this channel appears on the home screen
+     */
+    public CharSequence getDisplayName() {
+        return mValues.getAsString(Channels.COLUMN_DISPLAY_NAME);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_DESCRIPTION} for the channel. A short text
+     * explaining what this channel contains.
+     */
+    public CharSequence getDescription() {
+        return mValues.getAsString(Channels.COLUMN_DESCRIPTION);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_APP_LINK_INTENT_URI} for the channel.
+     */
+    public Uri getAppLinkIntentUri() {
+        String uri = mValues.getAsString(Channels.COLUMN_APP_LINK_INTENT_URI);
+        return uri == null ? null : Uri.parse(uri);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_APP_LINK_INTENT_URI} for the program.
+     */
+    public Intent getAppLinkIntent() throws URISyntaxException {
+        String uri = mValues.getAsString(Channels.COLUMN_APP_LINK_INTENT_URI);
+        return uri == null ? null : Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME);
+    }
+
+    /**
+     * This method should be called on a worker thread since decoding Bitmap is an expensive
+     * operation and therefore should not be performed on the main thread.
+     *
+     * @return The logo associated with this preview channel
+     */
+    @WorkerThread
+    public Bitmap getLogo(Context context) {
+        if (!mLogoFetched && mLogoImage == null) {
+            try {
+                mLogoImage = BitmapFactory.decodeStream(
+                        context.getContentResolver().openInputStream(
+                                TvContract.buildChannelLogoUri(getId())
+                        ));
+            } catch (FileNotFoundException | SQLiteException e) {
+                Log.e(TAG, "Logo for preview channel (ID:" + getId() + ") not found.", e);
+            }
+            mLogoFetched = true;
+        }
+        return mLogoImage;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    boolean isLogoChanged() {
+        return mLogoChanged;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    Uri getLogoUri() {
+        return mLogoUri;
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_DATA} for the channel.
+     */
+    public byte[] getInternalProviderDataByteArray() {
+        return mValues.getAsByteArray(Channels.COLUMN_INTERNAL_PROVIDER_DATA);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG1} for the channel.
+     */
+    public Long getInternalProviderFlag1() {
+        return mValues.getAsLong(Channels.COLUMN_INTERNAL_PROVIDER_FLAG1);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG2} for the channel.
+     */
+    public Long getInternalProviderFlag2() {
+        return mValues.getAsLong(Channels.COLUMN_INTERNAL_PROVIDER_FLAG2);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG3} for the channel.
+     */
+    public Long getInternalProviderFlag3() {
+        return mValues.getAsLong(Channels.COLUMN_INTERNAL_PROVIDER_FLAG3);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_FLAG4} for the channel.
+     */
+    public Long getInternalProviderFlag4() {
+        return mValues.getAsLong(Channels.COLUMN_INTERNAL_PROVIDER_FLAG4);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_INTERNAL_PROVIDER_ID} for the channel.
+     */
+    public String getInternalProviderId() {
+        return mValues.getAsString(Channels.COLUMN_INTERNAL_PROVIDER_ID);
+    }
+
+    /**
+     * @return The value of {@link Channels#COLUMN_BROWSABLE} for the channel. A preview channel
+     * is BROWABLE when it is visible on the TV home screen.
+     */
+    public boolean isBrowsable() {
+        Integer i = mValues.getAsInteger(Channels.COLUMN_BROWSABLE);
+        return i != null && i == IS_BROWSABLE;
+    }
+
+    @Override
+    public int hashCode() {
+        return mValues.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof PreviewChannel)) {
+            return false;
+        }
+        return mValues.equals(((PreviewChannel) other).mValues);
+    }
+
+    /**
+     * Indicates whether some other PreviewChannel has any set attribute that is different from
+     * this PreviewChannel's respective attributes. An attribute is considered "set" if its key
+     * is present in the ContentValues vector.
+     */
+    public boolean hasAnyUpdatedValues(PreviewChannel update) {
+        Set<String> updateKeys = update.mValues.keySet();
+        for (String key : updateKeys) {
+            Object updateValue = update.mValues.get(key);
+            Object currValue = mValues.get(key);
+            if (!Objects.deepEquals(updateValue, currValue)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "Channel{" + mValues.toString() + "}";
+    }
+
+    /**
+     * Used by {@link PreviewChannelHelper} to communicate PreviewChannel CRUD operations
+     * to the TvProvider. You never need to use this method unless you want to communicate to the
+     * TvProvider directly.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public ContentValues toContentValues() {
+        ContentValues values = new ContentValues(mValues);
+        return values;
+    }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    public static class Columns {
+        public static final String[] PROJECTION = {
+                Channels._ID,
+                Channels.COLUMN_PACKAGE_NAME,
+                Channels.COLUMN_TYPE,
+                Channels.COLUMN_DISPLAY_NAME,
+                Channels.COLUMN_DESCRIPTION,
+                Channels.COLUMN_APP_LINK_INTENT_URI,
+                Channels.COLUMN_INTERNAL_PROVIDER_ID,
+                Channels.COLUMN_INTERNAL_PROVIDER_DATA,
+                Channels.COLUMN_INTERNAL_PROVIDER_FLAG1,
+                Channels.COLUMN_INTERNAL_PROVIDER_FLAG2,
+                Channels.COLUMN_INTERNAL_PROVIDER_FLAG3,
+                Channels.COLUMN_INTERNAL_PROVIDER_FLAG4
+        };
+
+        public static final int COL_ID = 0;
+        public static final int COL_PACKAGE_NAME = 1;
+        public static final int COL_TYPE = 2;
+        public static final int COL_DISPLAY_NAME = 3;
+        public static final int COL_DESCRIPTION = 4;
+        public static final int COL_APP_LINK_INTENT_URI = 5;
+        public static final int COL_INTERNAL_PROVIDER_ID = 6;
+        public static final int COL_INTERNAL_PROVIDER_DATA = 7;
+        public static final int COL_INTERNAL_PROVIDER_FLAG1 = 8;
+        public static final int COL_INTERNAL_PROVIDER_FLAG2 = 9;
+        public static final int COL_INTERNAL_PROVIDER_FLAG3 = 10;
+        public static final int COL_INTERNAL_PROVIDER_FLAG4 = 11;
+    }
+
+    /**
+     * This builder makes it easy to create a PreviewChannel object by allowing you to chain
+     * setters. Even though this builder provides a no-arg constructor, certain fields are
+     * required or the {@link #build()} method will throw an exception. The required fields are
+     * displayName and appLinkIntentUri; use the respective methods to set them.
+     */
+    public static final class Builder {
+        private ContentValues mValues;
+        private Bitmap mLogoBitmap;
+        private Uri mLogoUri;
+
+        public Builder() {
+            mValues = new ContentValues();
+        }
+
+        public Builder(PreviewChannel other) {
+            mValues = new ContentValues(other.mValues);
+        }
+
+        private Builder setId(long id) {
+            mValues.put(Channels._ID, id);
+            return this;
+        }
+
+        /**
+         * Sets the package name of the Channel.
+         *
+         * @param packageName The value of {@link Channels#COLUMN_PACKAGE_NAME} for the channel.
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        Builder setPackageName(String packageName) {
+            mValues.put(Channels.COLUMN_PACKAGE_NAME, packageName);
+            return this;
+        }
+
+        // Private because this is always the same: setType(TvContractCompat.Channels.TYPE_PREVIEW)
+        private Builder setType(@Type String type) {
+            mValues.put(Channels.COLUMN_TYPE, type);
+            return this;
+        }
+
+        /**
+         * This is the name user sees when your channel appears on their TV home screen. For
+         * example "New Arrivals." This field is required.
+         *
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see TvContractCompat.Channels#COLUMN_DISPLAY_NAME
+         */
+        public Builder setDisplayName(CharSequence displayName) {
+            mValues.put(Channels.COLUMN_DISPLAY_NAME, displayName.toString());
+            return this;
+        }
+
+        /**
+         * It's good practice to include a general description of the programs in this channel.
+         *
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see TvContractCompat.Channels#COLUMN_DESCRIPTION
+         */
+        public Builder setDescription(CharSequence description) {
+            mValues.put(Channels.COLUMN_DESCRIPTION, description.toString());
+            return this;
+        }
+
+        /**
+         * When user clicks on this channel's logo, the system will send an Intent for your app to
+         * open an Activity with contents relevant to this channel. Hence, the Intent data you
+         * provide here must point to content relevant to this channel.
+         *
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setAppLinkIntent(Intent appLinkIntent) {
+            return setAppLinkIntentUri(Uri.parse(appLinkIntent.toUri(Intent.URI_INTENT_SCHEME)));
+        }
+
+        /**
+         * When user clicks on this channel's logo, the system will send an Intent for your app to
+         * open an Activity with contents relevant to this channel. Hence, the Uri you provide here
+         * must point to content relevant to this channel.
+         *
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see TvContractCompat.Channels#COLUMN_APP_LINK_INTENT_URI
+         */
+        public Builder setAppLinkIntentUri(Uri appLinkIntentUri) {
+            mValues.put(Channels.COLUMN_APP_LINK_INTENT_URI,
+                    null == appLinkIntentUri ? null : appLinkIntentUri.toString());
+            return this;
+        }
+
+        /**
+         * It is expected that your app or your server has its own internal representation
+         * (i.e. data structure) of channels. It is highly recommended that you store your
+         * app/server's channel ID here; so that you may easily relate this published preview
+         * channel with the corresponding channel from your server.
+         *
+         * The {@link PreviewChannelHelper#publishChannel(PreviewChannel) publish} method check this
+         * field to verify whether a preview channel being published would result in a duplicate.
+         * :
+         *
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see TvContractCompat.Channels#COLUMN_INTERNAL_PROVIDER_ID
+         */
+        public Builder setInternalProviderId(String internalProviderId) {
+            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_ID, internalProviderId);
+            return this;
+        }
+
+        /**
+         * This is one of the optional fields that your app may set. Use these fields at your
+         * discretion to help you remember important information about this channel.
+         *
+         * For example, if this channel needs a byte array that is expensive for your app to
+         * construct, you may choose to save it here.
+         *
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see TvContractCompat.Channels#COLUMN_INTERNAL_PROVIDER_DATA
+         */
+        public Builder setInternalProviderData(byte[] internalProviderData) {
+            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_DATA, internalProviderData);
+            return this;
+        }
+
+        /**
+         * This is one of the optional fields that your app may set. Use these fields at your
+         * discretion to help you remember important information about this channel.
+         *
+         * For example, you may use this flag to track additional data about this particular
+         * channel.
+         *
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see TvContractCompat.Channels#COLUMN_INTERNAL_PROVIDER_FLAG1
+         */
+        public Builder setInternalProviderFlag1(long flag) {
+            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_FLAG1, flag);
+            return this;
+        }
+
+        /**
+         * This is one of the optional fields that your app may set. Use these fields at your
+         * discretion to help you remember important information about this channel.
+         *
+         * For example, you may use this flag to track additional data about this particular
+         * channel.
+         *
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see TvContractCompat.Channels#COLUMN_INTERNAL_PROVIDER_FLAG2
+         */
+        public Builder setInternalProviderFlag2(long flag) {
+            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_FLAG2, flag);
+            return this;
+        }
+
+        /**
+         * This is one of the optional fields that your app may set. Use these fields at your
+         * discretion to help you remember important information about this channel.
+         *
+         * For example, you may use this flag to track additional data about this particular
+         * channel.
+         *
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see TvContractCompat.Channels#COLUMN_INTERNAL_PROVIDER_FLAG3
+         */
+        public Builder setInternalProviderFlag3(long flag) {
+            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_FLAG3, flag);
+            return this;
+        }
+
+        /**
+         * This is one of the optional fields that your app may set. Use these fields at your
+         * discretion to help you remember important information about this channel.
+         *
+         * For example, you may use this flag to track additional data about this particular
+         * channel.
+         *
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         * @see TvContractCompat.Channels#COLUMN_INTERNAL_PROVIDER_FLAG4
+         */
+        public Builder setInternalProviderFlag4(long flag) {
+            mValues.put(Channels.COLUMN_INTERNAL_PROVIDER_FLAG4, flag);
+            return this;
+        }
+
+        /**
+         * A logo visually identifies your channel. Hence, you should consider adding a unique logo
+         * to every channel you create, so user can quickly identify your channel.
+         *
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setLogo(@NonNull Bitmap logoImage) {
+            mLogoBitmap = logoImage;
+            mLogoUri = null;
+            return this;
+        }
+
+        /**
+         * A logo visually identifies your channel. Hence, you should consider adding a unique logo
+         * to every channel you create, so user can quickly identify your channel.
+         *
+         * @return This Builder object to allow for chaining of calls to builder methods.
+         */
+        public Builder setLogo(@NonNull Uri logoUri) {
+            mLogoUri = logoUri;
+            mLogoBitmap = null;
+            return this;
+        }
+
+        /**
+         * Takes the values of the Builder object and creates a PreviewChannel object.
+         *
+         * @return PreviewChannel object with values from the Builder.
+         */
+        public PreviewChannel build() {
+            setType(Channels.TYPE_PREVIEW);
+
+            if (TextUtils.isEmpty(mValues.getAsString(Channels.COLUMN_DISPLAY_NAME))) {
+                throw new IllegalStateException("Need channel name."
+                        + " Use method setDisplayName(String) to set it.");
+            }
+
+            if (TextUtils.isEmpty(mValues.getAsString(Channels.COLUMN_APP_LINK_INTENT_URI))) {
+                throw new IllegalStateException("Need app link intent uri for channel."
+                        + " Use method setAppLinkIntent or setAppLinkIntentUri to set it.");
+            }
+
+            PreviewChannel previewChannel = new PreviewChannel(this);
+            return previewChannel;
+        }
+    }
+}
diff --git a/tv-provider/src/main/java/android/support/media/tv/PreviewChannelHelper.java b/tv-provider/src/main/java/android/support/media/tv/PreviewChannelHelper.java
new file mode 100644
index 0000000..05fa9f5
--- /dev/null
+++ b/tv-provider/src/main/java/android/support/media/tv/PreviewChannelHelper.java
@@ -0,0 +1,475 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.media.tv;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.annotation.TargetApi;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.WorkerThread;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * From a user's perspective, the TV home screen has two types of channels: the single Live
+ * Channels row versus the App preview Channels. This class is concerned with App Channels; or more
+ * precisely: <i>your</i> app's preview Channels. In API 26+, all TV apps are allowed to create
+ * multiple channels and publish those Channels to the home screen.
+ * <p>
+ * This class provides convenience methods to help you publish, update and delete channels; add,
+ * update or remove programs in a channel. You do not need to know anything about Content
+ * Providers, Content Resolvers, Cursors or such to publish your channels. This class abstracts
+ * away all database interactions for you.
+ * <p>
+ * To make it easy for you to distinguish classes that help you build App Channels, the support
+ * library uses the prefix Preview- to denote the classes that pertain to app Channels. Hence,
+ * the classes {@link PreviewChannel} and {@link PreviewProgram} help your app add channels to the
+ * TV home page.
+ *
+ * All calls to methods in the class should be made on worker threads.
+ */
+
+@TargetApi(26)
+@WorkerThread
+public class PreviewChannelHelper {
+
+    private static final String TAG = "PreviewChannelHelper";
+    private static final int DEFAULT_URL_CONNNECTION_TIMEOUT_MILLIS =
+            (int) (3 * DateUtils.SECOND_IN_MILLIS);
+    private static final int DEFAULT_READ_TIMEOUT_MILLIS = (int) (10 * DateUtils.SECOND_IN_MILLIS);
+    private static final int INVALID_CONTENT_ID = -1;
+    private final int mUrlConnectionTimeoutMillis;
+    private final int mUrlReadTimeoutMillis;
+    private final Context mContext;
+
+    public PreviewChannelHelper(Context context) {
+        this(context, DEFAULT_URL_CONNNECTION_TIMEOUT_MILLIS, DEFAULT_READ_TIMEOUT_MILLIS);
+    }
+
+    /**
+     * @param urlConnectionTimeoutMillis see {@link URLConnection#setConnectTimeout(int)}
+     * @param urlReadTimeoutMillis       see {@link URLConnection#setReadTimeout(int)}
+     */
+    public PreviewChannelHelper(Context context, int urlConnectionTimeoutMillis,
+            int urlReadTimeoutMillis) {
+        mContext = context;
+        mUrlConnectionTimeoutMillis = urlConnectionTimeoutMillis;
+        mUrlReadTimeoutMillis = urlReadTimeoutMillis;
+    }
+
+    /**
+     * Publishing a channel to the TV home screen is a two step process: first, you add the
+     * channel to the TV content provider; second, you make the channel browsable (i.e. visible).
+     * {@link #publishChannel(PreviewChannel) This method} adds the channel to the
+     * TV content provider for you and returns a channelId. Next you must use the channelId
+     * to make the channel browsable.
+     * </br>
+     * There are two ways you can make a channel browsable:
+     * </br>
+     * a) For your first channel, simply ask the system to make the channel browsable:
+     * TvContractCompat.requestChannelBrowsable(context,channelId)
+     * </br>
+     * b) For any additional channel beyond the first channel, you must get permission
+     * from the user. So if this channel is not your first channel, you must request user
+     * permission through the following intent. So take the channelId returned by
+     * {@link #publishChannel(PreviewChannel) this method} and do the following
+     * inside an Activity or Fragment:
+     * </br>
+     * <pre>
+     * intent = new Intent(TvContractCompat.ACTION_REQUEST_CHANNEL_BROWSABLE);
+     * intent.putExtra(TvContractCompat.EXTRA_CHANNEL_ID, channelId);
+     * startActivityForResult(intent, REQUEST_CHANNEL_BROWSABLE);
+     * </pre>
+     *
+     * <p>
+     * Creating a PreviewChannel, you may pass to the builder a
+     * {@link PreviewChannel.Builder#setLogo(Uri) url as your logo}. In such case,
+     * {@link #updatePreviewChannel(long, PreviewChannel)} will load the logo over the network. To
+     * use your own networking code, override {@link #downloadBitmap(Uri)}.
+     *
+     * @return channelId or -1 if insertion fails. This is the id the system assigns to your
+     * published channel. You can use it later to get a reference to this published PreviewChannel.
+     */
+    public long publishChannel(@NonNull PreviewChannel channel) throws IOException {
+        try {
+            Uri channelUri = mContext.getContentResolver().insert(
+                    TvContractCompat.Channels.CONTENT_URI,
+                    channel.toContentValues());
+            if (null == channelUri || channelUri.equals(Uri.EMPTY)) {
+                throw new NullPointerException("Channel insertion failed");
+            }
+            long channelId = ContentUris.parseId(channelUri);
+            boolean logoAdded = addChannelLogo(channelId, channel);
+            // Rollback channel insertion if logo could not be added.
+            if (!logoAdded) {
+                deletePreviewChannel(channelId);
+                throw new IOException("Failed to add logo, so channel (ID="
+                        + channelId + ") was not created");
+            }
+            return channelId;
+        } catch (SecurityException e) {
+            Log.e(TAG, "Your app's ability to insert data into the TvProvider"
+                    + " may have been revoked.", e);
+        }
+        return INVALID_CONTENT_ID;
+    }
+
+    /**
+     * This is a convenience method that simply publishes your first channel for you. After calling
+     * {@link #publishChannel(PreviewChannel)} to add the channel to the TvProvider, it
+     * calls {@link TvContractCompat#requestChannelBrowsable(Context, long)} to make the channel
+     * visible.
+     * <p>
+     * Only use this method to publish your first channel as you do not need user permission to
+     * make your first channel browsable (i.e. visible on home screen). For additional channels,
+     * see the documentations for {@link #publishChannel(PreviewChannel)}.
+     *
+     * <p>
+     * Creating a PreviewChannel, you may pass to the builder a
+     * {@link PreviewChannel.Builder#setLogo(Uri) url as your logo}. In such case,
+     * {@link #updatePreviewChannel(long, PreviewChannel)} will load the logo over the network. To
+     * use your own networking code, override {@link #downloadBitmap(Uri)}.
+     *
+     * @return channelId: This is the id the system assigns to your published channel. You can
+     * use it later to get a reference to this published PreviewChannel.
+     */
+    public long publishDefaultChannel(@NonNull PreviewChannel channel)
+            throws IOException {
+        long channelId = publishChannel(channel);
+        TvContractCompat.requestChannelBrowsable(mContext, channelId);
+        return channelId;
+    }
+
+    /**
+     * The TvProvider does not allow select queries. Hence, unless you are querying for a
+     * {@link #getPreviewChannel(long) single PreviewChannel by id}, you must get all of
+     * your channels at once and then use the returned list as necessary.
+     */
+    public List<PreviewChannel> getAllChannels() {
+        Cursor cursor = mContext.getContentResolver()
+                .query(
+                        TvContractCompat.Channels.CONTENT_URI,
+                        PreviewChannel.Columns.PROJECTION,
+                        null,
+                        null,
+                        null);
+
+        List<PreviewChannel> channels = new ArrayList<>();
+        if (cursor != null && cursor.moveToFirst()) {
+            do {
+                channels.add(PreviewChannel.fromCursor(cursor));
+            } while (cursor.moveToNext());
+        }
+        return channels;
+    }
+
+    /**
+     * Retrieves a single preview channel from the TvProvider. When you publish a preview channel,
+     * the TvProvider assigns an ID to it. That's the channelId to use here.
+     *
+     * @param channelId ID of preview channel in TvProvider
+     * @return PreviewChannel or null if not found
+     */
+    public PreviewChannel getPreviewChannel(long channelId) {
+        PreviewChannel channel = null;
+        Uri channelUri = TvContractCompat.buildChannelUri(channelId);
+        Cursor cursor = mContext.getContentResolver()
+                .query(channelUri, PreviewChannel.Columns.PROJECTION, null, null, null);
+        if (cursor != null && cursor.moveToFirst()) {
+            channel = PreviewChannel.fromCursor(cursor);
+        }
+        return channel;
+    }
+
+    /**
+     * To update a preview channel, you need to use the {@link PreviewChannel.Builder} to set the
+     * attributes you wish to change. Then simply pass in the built channel and the channelId of the
+     * preview channel. (The channelId is the ID you received when you originally
+     * {@link #publishChannel(PreviewChannel) published} the preview channel.)
+     * <p>
+     * Creating a PreviewChannel, you may pass to the builder a
+     * {@link PreviewChannel.Builder#setLogo(Uri) url as your logo}. In such case,
+     * {@link #updatePreviewChannel(long, PreviewChannel)} will load the logo over the network. To
+     * use your own networking code, override {@link #downloadBitmap(Uri)}.
+     */
+    public void updatePreviewChannel(long channelId,
+            @NonNull PreviewChannel update) throws IOException {
+        // To avoid possibly expensive no-op updates, first check that the current content that's
+        // in the database is different from the new content to be added.
+        PreviewChannel curr = getPreviewChannel(channelId);
+        if (curr != null && curr.hasAnyUpdatedValues(update)) {
+            updatePreviewChannelInternal(channelId, update);
+        }
+        if (update.isLogoChanged()) {
+            boolean logoAdded = addChannelLogo(channelId, update);
+            if (!logoAdded) {
+                throw new IOException("Fail to update channel (ID=" + channelId + ") logo.");
+            }
+        }
+    }
+
+    /**
+     * Inner methods that does the actual work of updating a Preview Channel. The method is
+     * extracted to make {@link #updatePreviewChannel(long, PreviewChannel)} testable.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    protected void updatePreviewChannelInternal(long channelId, @NonNull PreviewChannel upgrade) {
+        mContext.getContentResolver().update(
+                TvContractCompat.buildChannelUri(channelId),
+                upgrade.toContentValues(),
+                null,
+                null);
+    }
+
+    /**
+     * Internally, a logo is added to a channel after the channel has been added to the TvProvider.
+     * This private method is called by one of the publish methods, to add a logo to the TvProvider
+     * and associate the logo to the given channel identified by channelId. Because each channel
+     * must have a logo, a NullPointerException is thrown if the channel being published has no
+     * associated logo to publish with it.
+     */
+    private boolean addChannelLogo(long channelId, @NonNull PreviewChannel channel) {
+        boolean result = false;
+        if (!channel.isLogoChanged()) {
+            return result;
+        }
+        Bitmap logo = channel.getLogo(mContext);
+        if (logo == null) {
+            logo = getLogoFromUri(channel.getLogoUri());
+        }
+        Uri logoUri = TvContractCompat.buildChannelLogoUri(channelId);
+        try (OutputStream outputStream = mContext.getContentResolver().openOutputStream(
+                logoUri)) {
+            result = logo.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
+            outputStream.flush();
+        } catch (SQLiteException | IOException | NullPointerException e) {
+            Log.i(TAG, "Failed to add logo to the published channel (ID= " + channelId + ")", e);
+        }
+        return result;
+    }
+
+    /**
+     * Handles the case where the Bitmap must be fetched from a known uri. First the
+     * method checks if the Uri is local. If not, the method makes a connection to fetch the Bitmap
+     * data from its remote location. To use your own networking implementation, simply override
+     * {@link #downloadBitmap(Uri)}
+     */
+    private Bitmap getLogoFromUri(@NonNull Uri logoUri) {
+        String scheme = logoUri.normalizeScheme().getScheme();
+        InputStream inputStream = null;
+        Bitmap logoImage = null;
+
+        try {
+            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)
+                    || ContentResolver.SCHEME_FILE.equals(scheme)
+                    || ContentResolver.SCHEME_CONTENT.equals(scheme)) {
+                // for local resource
+                inputStream = mContext.getContentResolver().openInputStream(logoUri);
+                logoImage = BitmapFactory.decodeStream(inputStream);
+            } else {
+                logoImage = downloadBitmap(logoUri);
+            }
+
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to get logo from the URI: " + logoUri, e);
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    // Do nothing
+                }
+            }
+        }
+        return logoImage;
+    }
+
+    /**
+     * Downloads a Bitmap from a remote server. It is declared protected to allow you
+     * to override it to use your own networking implementation if you so wish.
+     */
+    protected Bitmap downloadBitmap(@NonNull Uri logoUri) throws IOException {
+        URLConnection urlConnection = null;
+        InputStream inputStream = null;
+        Bitmap logoImage = null;
+        try {
+            // for remote resource
+            urlConnection = new URL(logoUri.toString()).openConnection();
+            urlConnection.setConnectTimeout(mUrlConnectionTimeoutMillis);
+            urlConnection.setReadTimeout(mUrlReadTimeoutMillis);
+            inputStream = urlConnection.getInputStream();
+            logoImage = BitmapFactory.decodeStream(inputStream);
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException e) {
+                    // Do nothing
+                }
+            }
+            if (urlConnection instanceof HttpURLConnection) {
+                ((HttpURLConnection) urlConnection).disconnect();
+            }
+        }
+        return logoImage;
+    }
+
+    /**
+     * Removes a preview channel from the system's content provider (aka TvProvider).
+     */
+    public void deletePreviewChannel(long channelId) {
+        mContext.getContentResolver().delete(
+                TvContractCompat.buildChannelUri(channelId),
+                null,
+                null);
+    }
+
+    /**
+     * Adds programs to a preview channel.
+     */
+    public long publishPreviewProgram(@NonNull PreviewProgram program) {
+        try {
+            Uri programUri = mContext.getContentResolver().insert(
+                    TvContractCompat.PreviewPrograms.CONTENT_URI,
+                    program.toContentValues());
+            long programId = ContentUris.parseId(programUri);
+            return programId;
+        } catch (SecurityException e) {
+            Log.e(TAG, "Your app's ability to insert data into the TvProvider"
+                    + " may have been revoked.", e);
+        }
+        return INVALID_CONTENT_ID;
+    }
+
+    /**
+     * Retrieves a single preview program from the system content provider (aka TvProvider).
+     */
+    public PreviewProgram getPreviewProgram(long programId) {
+        PreviewProgram program = null;
+        Uri programUri = TvContractCompat.buildPreviewProgramUri(programId);
+        Cursor cursor = mContext.getContentResolver().query(programUri, null, null, null, null);
+        if (cursor != null && cursor.moveToFirst()) {
+            program = PreviewProgram.fromCursor(cursor);
+        }
+        return program;
+    }
+
+    /**
+     * Updates programs in a preview channel.
+     */
+    public void updatePreviewProgram(long programId, @NonNull PreviewProgram update) {
+        // To avoid possibly expensive no-op updates, first check that the current content that's
+        // in the database is different from the new content to be added.
+        PreviewProgram curr = getPreviewProgram(programId);
+        if (curr != null && curr.hasAnyUpdatedValues(update)) {
+            updatePreviewProgramInternal(programId, update);
+        }
+    }
+
+    /**
+     * Inner methods that does the actual work of updating a Preview Program. The method is
+     * extracted to make {@link #updatePreviewProgram(long, PreviewProgram)} testable.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    void updatePreviewProgramInternal(long programId, @NonNull PreviewProgram upgrade) {
+        mContext.getContentResolver().update(
+                TvContractCompat.buildPreviewProgramUri(programId),
+                upgrade.toContentValues(), null, null);
+    }
+
+    /**
+     * Removes programs from a preview channel.
+     */
+    public void deletePreviewProgram(long programId) {
+        mContext.getContentResolver().delete(
+                TvContractCompat.buildPreviewProgramUri(programId), null, null);
+    }
+
+    /**
+     * Adds a program to the Watch Next channel
+     */
+    public long publishWatchNextProgram(@NonNull WatchNextProgram program) {
+        try {
+            Uri programUri = mContext.getContentResolver().insert(
+                    TvContractCompat.WatchNextPrograms.CONTENT_URI, program.toContentValues());
+            return ContentUris.parseId(programUri);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Your app's ability to insert data into the TvProvider"
+                    + " may have been revoked.", e);
+        }
+        return INVALID_CONTENT_ID;
+    }
+
+    /**
+     * Retrieves a single WatchNext program from the system content provider (aka TvProvider).
+     */
+    public WatchNextProgram getWatchNextProgram(long programId) {
+        WatchNextProgram program = null;
+        Uri programUri = TvContractCompat.buildWatchNextProgramUri(programId);
+        Cursor cursor = mContext.getContentResolver().query(programUri, null, null, null, null);
+        if (cursor != null && cursor.moveToFirst()) {
+            program = WatchNextProgram.fromCursor(cursor);
+        }
+        return program;
+    }
+
+    /**
+     * Updates a WatchNext program.
+     */
+    public void updateWatchNextProgram(@NonNull WatchNextProgram upgrade, long programId) {
+        // To avoid possibly expensive no-op updates, first check that the current content that's in
+        // the database is different from the new content to be added.
+        WatchNextProgram curr = getWatchNextProgram(programId);
+        if (curr != null && curr.hasAnyUpdatedValues(upgrade)) {
+            updateWatchNextProgram(programId, upgrade);
+        }
+    }
+
+    /**
+     * Inner methods that does the actual work of updating a Watch Next Program. The method is
+     * extracted to make {@link #updateWatchNextProgram(WatchNextProgram, long)} testable.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY_GROUP)
+    void updateWatchNextProgram(long programId, @NonNull WatchNextProgram upgrade) {
+        mContext.getContentResolver().update(
+                TvContractCompat.buildWatchNextProgramUri(programId),
+                upgrade.toContentValues(), null, null);
+    }
+}
diff --git a/tv-provider/src/main/java/android/support/media/tv/PreviewProgram.java b/tv-provider/src/main/java/android/support/media/tv/PreviewProgram.java
index 2536f75..f44e18f 100644
--- a/tv-provider/src/main/java/android/support/media/tv/PreviewProgram.java
+++ b/tv-provider/src/main/java/android/support/media/tv/PreviewProgram.java
@@ -23,6 +23,9 @@
 import android.support.annotation.RestrictTo;
 import android.support.media.tv.TvContractCompat.PreviewPrograms;
 
+import java.util.Objects;
+import java.util.Set;
+
 /**
  * A convenience class to access {@link PreviewPrograms} entries in the system content
  * provider.
@@ -108,6 +111,23 @@
         return mValues.equals(((PreviewProgram) other).mValues);
     }
 
+    /**
+     * Indicates whether some other PreviewProgram has any set attribute that is different from
+     * this PreviewProgram's respective attributes. An attribute is considered "set" if its key
+     * is present in the ContentValues vector.
+     */
+    public boolean hasAnyUpdatedValues(PreviewProgram update) {
+        Set<String> updateKeys = update.mValues.keySet();
+        for (String key : updateKeys) {
+            Object updateValue = update.mValues.get(key);
+            Object currValue = mValues.get(key);
+            if (!Objects.deepEquals(updateValue, currValue)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     public String toString() {
         return "PreviewProgram{" + mValues.toString() + "}";
@@ -164,7 +184,7 @@
     }
 
     private static String[] getProjection() {
-        String[] oColumns = new String[] {
+        String[] oColumns = new String[]{
                 PreviewPrograms.COLUMN_CHANNEL_ID,
                 PreviewPrograms.COLUMN_WEIGHT,
         };
@@ -184,6 +204,7 @@
 
         /**
          * Creates a new Builder object with values copied from another Program.
+         *
          * @param other The Program you're copying from.
          */
         public Builder(PreviewProgram other) {
diff --git a/tv-provider/src/main/java/android/support/media/tv/WatchNextProgram.java b/tv-provider/src/main/java/android/support/media/tv/WatchNextProgram.java
index e5d12b2..cb317b7 100644
--- a/tv-provider/src/main/java/android/support/media/tv/WatchNextProgram.java
+++ b/tv-provider/src/main/java/android/support/media/tv/WatchNextProgram.java
@@ -26,6 +26,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.Set;
 
 /**
  * A convenience class to access {@link WatchNextPrograms} entries in the system content
@@ -94,7 +96,8 @@
     })
     @Retention(RetentionPolicy.SOURCE)
     @RestrictTo(LIBRARY_GROUP)
-    public @interface WatchNextType {}
+    public @interface WatchNextType {
+    }
 
     /**
      * The unknown watch next type. Use this type when the actual type is not known.
@@ -131,6 +134,23 @@
         return mValues.equals(((WatchNextProgram) other).mValues);
     }
 
+    /**
+     * Indicates whether some other WatchNextProgram has any set attribute that is different from
+     * this WatchNextProgram's respective attributes. An attribute is considered "set" if its key
+     * is present in the ContentValues vector.
+     */
+    public boolean hasAnyUpdatedValues(WatchNextProgram update) {
+        Set<String> updateKeys = update.mValues.keySet();
+        for (String key : updateKeys) {
+            Object updateValue = update.mValues.get(key);
+            Object currValue = mValues.get(key);
+            if (!Objects.deepEquals(updateValue, currValue)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     public String toString() {
         return "WatchNextProgram{" + mValues.toString() + "}";
@@ -188,7 +208,7 @@
     }
 
     private static String[] getProjection() {
-        String[] oColumns = new String[] {
+        String[] oColumns = new String[]{
                 WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE,
                 WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS,
         };
@@ -208,6 +228,7 @@
 
         /**
          * Creates a new Builder object with values copied from another Program.
+         *
          * @param other The Program you're copying from.
          */
         public Builder(WatchNextProgram other) {
@@ -235,7 +256,8 @@
          * Sets the time when the program is going to begin in milliseconds since the epoch.
          *
          * @param lastEngagementTimeUtcMillis The value of
-         * {@link WatchNextPrograms#COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS} for the program.
+         *      {@link WatchNextPrograms#COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS}
+         *      for the program.
          * @return This Builder object to allow for chaining of calls to builder methods.
          */
         public Builder setLastEngagementTimeUtcMillis(long lastEngagementTimeUtcMillis) {
diff --git a/v13/build.gradle b/v13/build.gradle
index 82a48bd..cb9cdbf 100644
--- a/v13/build.gradle
+++ b/v13/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/v14/preference/build.gradle b/v14/preference/build.gradle
index 86036bd..43f6e36 100644
--- a/v14/preference/build.gradle
+++ b/v14/preference/build.gradle
@@ -14,8 +14,8 @@
  * limitations under the License
  */
 
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/v4/build.gradle b/v4/build.gradle
index 8725a46..640c5fe 100644
--- a/v4/build.gradle
+++ b/v4/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/v7/appcompat/build.gradle b/v7/appcompat/build.gradle
index 799b7b3..2f50fff 100644
--- a/v7/appcompat/build.gradle
+++ b/v7/appcompat/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
@@ -17,7 +17,7 @@
     androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
-    androidTestImplementation project(':support-testutils'), {
+    androidTestImplementation project(':internal-testutils'), {
         exclude group: 'com.android.support', module: 'appcompat-v7'
     }
 }
@@ -36,6 +36,10 @@
         additionalParameters "--no-version-vectors"
         noCompress 'ttf'
     }
+
+    buildTypes.all {
+        consumerProguardFiles("proguard-rules.pro")
+    }
 }
 
 supportLibrary {
diff --git a/v7/appcompat/proguard-rules.pro b/v7/appcompat/proguard-rules.pro
new file mode 100644
index 0000000..98c23e5
--- /dev/null
+++ b/v7/appcompat/proguard-rules.pro
@@ -0,0 +1,17 @@
+# 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.
+
+# Ensure that reflectively-loaded inflater is not obfuscated. This can be
+# removed when we stop supporting AAPT1 builds.
+-keepnames class android.support.v7.app.AppCompatViewInflater
diff --git a/v7/appcompat/src/androidTest/java/android/support/v7/app/NightModeTestCase.java b/v7/appcompat/src/androidTest/java/android/support/v7/app/NightModeTestCase.java
index d42174f..7f636bd 100644
--- a/v7/appcompat/src/androidTest/java/android/support/v7/app/NightModeTestCase.java
+++ b/v7/appcompat/src/androidTest/java/android/support/v7/app/NightModeTestCase.java
@@ -30,8 +30,6 @@
 import android.support.test.filters.LargeTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.testutils.AppCompatActivityUtils;
-import android.support.testutils.RecreatedAppCompatActivity;
 import android.support.v4.content.ContextCompat;
 import android.support.v7.appcompat.test.R;
 
@@ -43,6 +41,9 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import androidx.testutils.AppCompatActivityUtils;
+import androidx.testutils.RecreatedAppCompatActivity;
+
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class NightModeTestCase {
diff --git a/v7/appcompat/src/androidTest/java/android/support/v7/testutils/BaseTestActivity.java b/v7/appcompat/src/androidTest/java/android/support/v7/testutils/BaseTestActivity.java
index e4dbf26..c880768 100644
--- a/v7/appcompat/src/androidTest/java/android/support/v7/testutils/BaseTestActivity.java
+++ b/v7/appcompat/src/androidTest/java/android/support/v7/testutils/BaseTestActivity.java
@@ -19,7 +19,6 @@
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.testutils.RecreatedAppCompatActivity;
 import android.support.v7.app.AppCompatCallback;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.view.ActionMode;
@@ -28,6 +27,8 @@
 import android.view.MenuItem;
 import android.view.WindowManager;
 
+import androidx.testutils.RecreatedAppCompatActivity;
+
 public abstract class BaseTestActivity extends RecreatedAppCompatActivity {
 
     private Menu mMenu;
diff --git a/v7/appcompat/src/androidTest/java/android/support/v7/widget/PopupMenuTest.java b/v7/appcompat/src/androidTest/java/android/support/v7/widget/PopupMenuTest.java
index cae216d..c1326ac 100644
--- a/v7/appcompat/src/androidTest/java/android/support/v7/widget/PopupMenuTest.java
+++ b/v7/appcompat/src/androidTest/java/android/support/v7/widget/PopupMenuTest.java
@@ -56,7 +56,6 @@
 import android.support.test.filters.SdkSuppress;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.testutils.PollingCheck;
 import android.support.v4.view.MenuItemCompat;
 import android.support.v7.appcompat.test.R;
 import android.text.TextUtils;
@@ -80,6 +79,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import androidx.testutils.PollingCheck;
+
 @RunWith(AndroidJUnit4.class)
 public class PopupMenuTest {
     @Rule
diff --git a/v7/appcompat/src/androidTest/java/android/support/v7/widget/SearchView_CursorTest.java b/v7/appcompat/src/androidTest/java/android/support/v7/widget/SearchView_CursorTest.java
index ea919e8..7d61d54 100644
--- a/v7/appcompat/src/androidTest/java/android/support/v7/widget/SearchView_CursorTest.java
+++ b/v7/appcompat/src/androidTest/java/android/support/v7/widget/SearchView_CursorTest.java
@@ -36,7 +36,6 @@
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.testutils.PollingCheck;
 import android.support.v4.widget.CursorAdapter;
 import android.support.v4.widget.SimpleCursorAdapter;
 import android.support.v7.appcompat.test.R;
@@ -50,6 +49,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import androidx.testutils.PollingCheck;
+
 /**
  * Test {@link SearchView} with {@link Cursor}-backed suggestions adapter.
  */
diff --git a/v7/appcompat/src/main/java/android/support/v7/app/WindowDecorActionBar.java b/v7/appcompat/src/main/java/android/support/v7/app/WindowDecorActionBar.java
index 1c17922..db8c1a2 100644
--- a/v7/appcompat/src/main/java/android/support/v7/app/WindowDecorActionBar.java
+++ b/v7/appcompat/src/main/java/android/support/v7/app/WindowDecorActionBar.java
@@ -239,7 +239,7 @@
             return ((Toolbar) view).getWrapper();
         } else {
             throw new IllegalStateException("Can't make a decor toolbar out of " +
-                    view != null ? view.getClass().getSimpleName() : "null");
+                    (view != null ? view.getClass().getSimpleName() : "null"));
         }
     }
 
diff --git a/v7/appcompat/src/main/java/android/support/v7/view/menu/ListMenuItemView.java b/v7/appcompat/src/main/java/android/support/v7/view/menu/ListMenuItemView.java
index 0b5cb48..8e58754 100644
--- a/v7/appcompat/src/main/java/android/support/v7/view/menu/ListMenuItemView.java
+++ b/v7/appcompat/src/main/java/android/support/v7/view/menu/ListMenuItemView.java
@@ -196,9 +196,8 @@
         if (checkable) {
             compoundButton.setChecked(mItemData.isChecked());
 
-            final int newVisibility = checkable ? VISIBLE : GONE;
-            if (compoundButton.getVisibility() != newVisibility) {
-                compoundButton.setVisibility(newVisibility);
+            if (compoundButton.getVisibility() != VISIBLE) {
+                compoundButton.setVisibility(VISIBLE);
             }
 
             // Make sure the other compound button isn't visible
diff --git a/v7/appcompat/src/main/java/android/support/v7/widget/ActivityChooserView.java b/v7/appcompat/src/main/java/android/support/v7/widget/ActivityChooserView.java
index 9979363..1121966 100644
--- a/v7/appcompat/src/main/java/android/support/v7/widget/ActivityChooserView.java
+++ b/v7/appcompat/src/main/java/android/support/v7/widget/ActivityChooserView.java
@@ -90,7 +90,7 @@
     /**
      * The content of this view.
      */
-    private final LinearLayoutCompat mActivityChooserContent;
+    private final View mActivityChooserContent;
 
     /**
      * Stores the background drawable to allow hiding and latter showing.
diff --git a/v7/cardview/build.gradle b/v7/cardview/build.gradle
index 88ce452..cb597ad 100644
--- a/v7/cardview/build.gradle
+++ b/v7/cardview/build.gradle
@@ -1,5 +1,5 @@
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/v7/gridlayout/build.gradle b/v7/gridlayout/build.gradle
index 329cb00..85021b1 100644
--- a/v7/gridlayout/build.gradle
+++ b/v7/gridlayout/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/v7/mediarouter/build.gradle b/v7/mediarouter/build.gradle
index f28938b..3d19227 100644
--- a/v7/mediarouter/build.gradle
+++ b/v7/mediarouter/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/v7/mediarouter/src/main/res/values/strings.xml b/v7/mediarouter/src/main/res/values/strings.xml
index 630a482..4dd0f8a 100644
--- a/v7/mediarouter/src/main/res/values/strings.xml
+++ b/v7/mediarouter/src/main/res/values/strings.xml
@@ -46,7 +46,7 @@
     <!-- Button to disconnect from a media route.  [CHAR LIMIT=30] -->
     <string name="mr_controller_disconnect">Disconnect</string>
 
-    <!-- Button to stop playback and disconnect from a media route. [CHAR LIMIT=30] -->
+    <!-- Button to stop playback and disconnect from a media route. [CHAR LIMIT=32] -->
     <string name="mr_controller_stop_casting">Stop casting</string>
 
     <!-- Content description for accessibility (not shown on the screen): dialog close button. [CHAR LIMIT=NONE] -->
diff --git a/v7/palette/build.gradle b/v7/palette/build.gradle
index 8829dcb..9c91601 100644
--- a/v7/palette/build.gradle
+++ b/v7/palette/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/v7/preference/build.gradle b/v7/preference/build.gradle
index 7707883..a0c9bd9 100644
--- a/v7/preference/build.gradle
+++ b/v7/preference/build.gradle
@@ -14,9 +14,9 @@
  * limitations under the License
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/v7/preference/res/values/attrs.xml b/v7/preference/res/values/attrs.xml
index bb52593..332a3df 100644
--- a/v7/preference/res/values/attrs.xml
+++ b/v7/preference/res/values/attrs.xml
@@ -176,6 +176,9 @@
         <attr name="iconSpaceReserved" format="boolean" />
         <attr name="android:iconSpaceReserved" />
 
+        <!-- Whether the Preference is visible. By default, this is set to true. -->
+        <attr name="isPreferenceVisible" format="boolean" />
+
     </declare-styleable>
 
     <!-- Base attributes available to CheckBoxPreference. -->
diff --git a/v7/preference/src/androidTest/java/android/support/v7/preference/tests/PreferenceVisibilityTest.java b/v7/preference/src/androidTest/java/android/support/v7/preference/tests/PreferenceVisibilityTest.java
new file mode 100644
index 0000000..9000f15
--- /dev/null
+++ b/v7/preference/src/androidTest/java/android/support/v7/preference/tests/PreferenceVisibilityTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.preference.tests;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v7.preference.test.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for {@link android.support.v7.preference.Preference} visibility set with XML.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PreferenceVisibilityTest {
+
+    @Test
+    @UiThreadTest
+    public void testPreferencesAreCreatedWithTheVisibilitySetInXml() {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final PreferenceManager manager = new PreferenceManager(context);
+        final PreferenceScreen screen = manager.inflateFromResource(context,
+                R.layout.test_visibility,
+                null);
+
+        // Preference without visibility set should be visible
+        assertTrue(screen.getPreference(0).isVisible());
+        // Preference with visibility set to true should be visible
+        assertTrue(screen.getPreference(1).isVisible());
+        // Preference with visibility set to false should not be invisible
+        assertFalse(screen.getPreference(2).isVisible());
+    }
+}
diff --git a/v7/preference/src/androidTest/res/layout/test_visibility.xml b/v7/preference/src/androidTest/res/layout/test_visibility.xml
new file mode 100644
index 0000000..5623cd1
--- /dev/null
+++ b/v7/preference/src/androidTest/res/layout/test_visibility.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+   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.
+-->
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent">
+
+    <Preference android:summary="This preference is visible by default" />
+
+    <Preference
+        android:summary="This preference is forced to be visible"
+        app:isPreferenceVisible="true" />
+
+    <Preference
+        android:summary="This preference is invisible"
+        app:isPreferenceVisible="false" />
+</PreferenceScreen>
diff --git a/v7/preference/src/main/java/android/support/v7/preference/Preference.java b/v7/preference/src/main/java/android/support/v7/preference/Preference.java
index 88262cd..79fbceb 100644
--- a/v7/preference/src/main/java/android/support/v7/preference/Preference.java
+++ b/v7/preference/src/main/java/android/support/v7/preference/Preference.java
@@ -323,6 +323,9 @@
         mIconSpaceReserved = TypedArrayUtils.getBoolean(a, R.styleable.Preference_iconSpaceReserved,
                 R.styleable.Preference_android_iconSpaceReserved, false);
 
+        mVisible = TypedArrayUtils.getBoolean(a, R.styleable.Preference_isPreferenceVisible,
+                R.styleable.Preference_isPreferenceVisible, true);
+
         a.recycle();
     }
 
@@ -871,6 +874,8 @@
      * {@link PreferenceFragmentCompat#findPreference(CharSequence)}.
      *
      * @param visible Set false if this preference should be hidden from the list.
+     *
+     * @attr ref R.styleable#Preference_isPreferenceVisible
      */
     public final void setVisible(boolean visible) {
         if (mVisible != visible) {
diff --git a/v7/recyclerview/build.gradle b/v7/recyclerview/build.gradle
index e58e5b1..5f7630c 100644
--- a/v7/recyclerview/build.gradle
+++ b/v7/recyclerview/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
@@ -18,7 +18,7 @@
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(JUNIT)
     androidTestImplementation(KOTLIN_STDLIB)
-    androidTestImplementation(project(":support-testutils"))
+    androidTestImplementation(project(":internal-testutils"))
 
     testImplementation(JUNIT)
     testImplementation(MOCKITO_CORE)
diff --git a/v7/recyclerview/src/androidTest/java/android/support/v7/recyclerview/extensions/AsyncListDifferTest.kt b/v7/recyclerview/src/androidTest/java/android/support/v7/recyclerview/extensions/AsyncListDifferTest.kt
index 2518065..7d2ec60 100644
--- a/v7/recyclerview/src/androidTest/java/android/support/v7/recyclerview/extensions/AsyncListDifferTest.kt
+++ b/v7/recyclerview/src/androidTest/java/android/support/v7/recyclerview/extensions/AsyncListDifferTest.kt
@@ -170,6 +170,43 @@
         verifyNoMoreInteractions(callback)
     }
 
+    @Test
+    fun singleChangePayload() {
+        val callback = mock(ListUpdateCallback::class.java)
+        val helper = createHelper(callback, STRING_DIFF_CALLBACK)
+
+        helper.submitList(listOf("a", "b"))
+        verify(callback).onInserted(0, 2)
+        verifyNoMoreInteractions(callback)
+        drain()
+        verifyNoMoreInteractions(callback)
+
+        helper.submitList(listOf("a", "beta"))
+        verifyNoMoreInteractions(callback)
+        drain()
+        verify(callback).onChanged(1, 1, "eta")
+        verifyNoMoreInteractions(callback)
+    }
+
+    @Test
+    fun multiChangePayload() {
+        val callback = mock(ListUpdateCallback::class.java)
+        val helper = createHelper(callback, STRING_DIFF_CALLBACK)
+
+        helper.submitList(listOf("a", "b"))
+        verify(callback).onInserted(0, 2)
+        verifyNoMoreInteractions(callback)
+        drain()
+        verifyNoMoreInteractions(callback)
+
+        helper.submitList(listOf("alpha", "beta"))
+        verifyNoMoreInteractions(callback)
+        drain()
+        verify(callback).onChanged(1, 1, "eta")
+        verify(callback).onChanged(0, 1, "lpha")
+        verifyNoMoreInteractions(callback)
+    }
+
     private fun drain() {
         var executed: Boolean
         do {
@@ -181,12 +218,21 @@
     companion object {
         private val STRING_DIFF_CALLBACK = object : DiffUtil.ItemCallback<String>() {
             override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
-                return oldItem == newItem
+                // items are the same if first char is the same
+                return oldItem[0] == newItem[0]
             }
 
             override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
                 return oldItem == newItem
             }
+
+            override fun getChangePayload(oldItem: String, newItem: String): Any? {
+                if (newItem.startsWith(oldItem)) {
+                    // new string is appended, return added portion on the end
+                    return newItem.subSequence(oldItem.length, newItem.length)
+                }
+                return null
+            }
         }
 
         private val IGNORE_CALLBACK = object : ListUpdateCallback {
diff --git a/v7/recyclerview/src/androidTest/java/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java b/v7/recyclerview/src/androidTest/java/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
index e47a480..cf6e228 100644
--- a/v7/recyclerview/src/androidTest/java/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
+++ b/v7/recyclerview/src/androidTest/java/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
@@ -36,7 +36,6 @@
 import android.support.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
-import android.support.testutils.PollingCheck;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.recyclerview.test.R;
 import android.util.Log;
@@ -63,6 +62,8 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import androidx.testutils.PollingCheck;
+
 abstract public class BaseRecyclerViewInstrumentationTest {
 
     private static final String TAG = "RecyclerViewTest";
@@ -80,7 +81,8 @@
     Thread mInstrumentationThread;
 
     @Rule
-    public ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule(TestActivity.class);
+    public ActivityTestRule<TestActivity> mActivityRule =
+            new ActivityTestRule<>(TestActivity.class);
 
     public BaseRecyclerViewInstrumentationTest() {
         this(false);
diff --git a/v7/recyclerview/src/androidTest/java/android/support/v7/widget/helper/ItemTouchHelperTest.java b/v7/recyclerview/src/androidTest/java/android/support/v7/widget/helper/ItemTouchHelperTest.java
index 99d1066..6d73fbc 100644
--- a/v7/recyclerview/src/androidTest/java/android/support/v7/widget/helper/ItemTouchHelperTest.java
+++ b/v7/recyclerview/src/androidTest/java/android/support/v7/widget/helper/ItemTouchHelperTest.java
@@ -32,7 +32,6 @@
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.Suppress;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.testutils.PollingCheck;
 import android.support.v4.util.Pair;
 import android.support.v7.util.TouchUtils;
 import android.support.v7.widget.BaseRecyclerViewInstrumentationTest;
@@ -47,6 +46,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import androidx.testutils.PollingCheck;
+
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class ItemTouchHelperTest extends BaseRecyclerViewInstrumentationTest {
diff --git a/v7/recyclerview/src/main/java/android/support/v7/recyclerview/extensions/AsyncListDiffer.java b/v7/recyclerview/src/main/java/android/support/v7/recyclerview/extensions/AsyncListDiffer.java
index 5259b4f..fa463d9 100644
--- a/v7/recyclerview/src/main/java/android/support/v7/recyclerview/extensions/AsyncListDiffer.java
+++ b/v7/recyclerview/src/main/java/android/support/v7/recyclerview/extensions/AsyncListDiffer.java
@@ -240,6 +240,13 @@
                         return mConfig.getDiffCallback().areContentsTheSame(
                                 oldList.get(oldItemPosition), newList.get(newItemPosition));
                     }
+
+                    @Nullable
+                    @Override
+                    public Object getChangePayload(int oldItemPosition, int newItemPosition) {
+                        return mConfig.getDiffCallback().getChangePayload(
+                                oldList.get(oldItemPosition), newList.get(newItemPosition));
+                    }
                 });
 
                 mConfig.getMainThreadExecutor().execute(new Runnable() {
diff --git a/viewpager/build.gradle b/viewpager/build.gradle
index fdf3b29..5da06de 100644
--- a/viewpager/build.gradle
+++ b/viewpager/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/viewpager2/build.gradle b/viewpager2/build.gradle
index 7f75d09..a3106f0 100644
--- a/viewpager2/build.gradle
+++ b/viewpager2/build.gradle
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
 }
 
 dependencies {
-    api(project(":support-core-utils"))
+    api(project(":support-fragment"))
     api(project(":recyclerview-v7"))
 
     androidTestImplementation(TEST_RUNNER)
diff --git a/viewpager2/src/androidTest/AndroidManifest.xml b/viewpager2/src/androidTest/AndroidManifest.xml
index ac723e6..22028ad 100755
--- a/viewpager2/src/androidTest/AndroidManifest.xml
+++ b/viewpager2/src/androidTest/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="androidx.widget.viewpager2.test">
+          package="androidx.viewpager2.test">
     <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application android:supportsRtl="true">
-        <activity android:name="androidx.widget.viewpager2.tests.TestActivity"/>
+        <activity android:name="androidx.viewpager2.widget.tests.TestActivity"/>
     </application>
 </manifest>
\ No newline at end of file
diff --git a/viewpager2/src/androidTest/java/androidx/widget/viewpager2/tests/TestActivity.java b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/tests/TestActivity.java
similarity index 81%
rename from viewpager2/src/androidTest/java/androidx/widget/viewpager2/tests/TestActivity.java
rename to viewpager2/src/androidTest/java/androidx/viewpager2/widget/tests/TestActivity.java
index 351ad9a..f94d9d6 100644
--- a/viewpager2/src/androidTest/java/androidx/widget/viewpager2/tests/TestActivity.java
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/tests/TestActivity.java
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package androidx.widget.viewpager2.tests;
+package androidx.viewpager2.widget.tests;
 
-import android.app.Activity;
 import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
 
-import androidx.widget.viewpager2.test.R;
+import androidx.viewpager2.test.R;
 
-public class TestActivity extends Activity {
+public class TestActivity extends FragmentActivity {
     @Override
     public void onCreate(Bundle bundle) {
         super.onCreate(bundle);
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/tests/ViewPager2Tests.java b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/tests/ViewPager2Tests.java
new file mode 100644
index 0000000..2355df1
--- /dev/null
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/tests/ViewPager2Tests.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 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 androidx.viewpager2.widget.tests;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
+import static android.view.View.OVER_SCROLL_NEVER;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.ViewActions;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.Adapter;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import androidx.viewpager2.test.R;
+import androidx.viewpager2.widget.ViewPager2;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ViewPager2Tests {
+    private static final Random RANDOM = new Random();
+    private static final int[] sColors = {
+            Color.parseColor("#BBA9FF00"),
+            Color.parseColor("#BB00E87E"),
+            Color.parseColor("#BB00C7FF"),
+            Color.parseColor("#BBB30CE8"),
+            Color.parseColor("#BBFF00D0")};
+
+    @Rule
+    public final ActivityTestRule<TestActivity> mActivityTestRule;
+    @Rule
+    public ExpectedException mExpectedException = ExpectedException.none();
+
+    private ViewPager2 mViewPager;
+
+    // allows to wait until swipe operation is finished (Smooth Scroller done)
+    private CountDownLatch mStableAfterSwipe;
+
+    public ViewPager2Tests() {
+        mActivityTestRule = new ActivityTestRule<>(TestActivity.class);
+    }
+
+    @Before
+    public void setUp() {
+        mViewPager = mActivityTestRule.getActivity().findViewById(R.id.view_pager);
+
+        mViewPager.addOnScrollListener(new RecyclerView.OnScrollListener() {
+            @Override
+            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+                // coming to idle from another state (dragging or setting) means we're stable now
+                if (newState == SCROLL_STATE_IDLE) {
+                    mStableAfterSwipe.countDown();
+                }
+            }
+        });
+
+        final long seed = RANDOM.nextLong();
+        RANDOM.setSeed(seed);
+        Log.i(getClass().getName(), "Random seed: " + seed);
+    }
+
+    public static class PageFragment extends Fragment {
+        private static final String KEY_VALUE = "value";
+
+        public interface EventListener {
+            void onEvent(PageFragment fragment);
+
+            EventListener NO_OP = new EventListener() {
+                @Override
+                public void onEvent(PageFragment fragment) {
+                    // do nothing
+                }
+            };
+        }
+
+        private EventListener mOnAttachListener = EventListener.NO_OP;
+        private EventListener mOnDestroyListener = EventListener.NO_OP;
+
+        private int mPosition;
+        private int mValue;
+
+        public static PageFragment create(int position, int value) {
+            PageFragment result = new PageFragment();
+            Bundle args = new Bundle(1);
+            args.putInt(KEY_VALUE, value);
+            result.setArguments(args);
+            result.mPosition = position;
+            return result;
+        }
+
+        @Override
+        public void onAttach(Context context) {
+            super.onAttach(context);
+            mOnAttachListener.onEvent(this);
+        }
+
+        @NonNull
+        @Override
+        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+                @Nullable Bundle savedInstanceState) {
+            return inflater.inflate(R.layout.item_test_layout, container, false);
+        }
+
+        @Override
+        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+            Bundle data = savedInstanceState != null ? savedInstanceState : getArguments();
+            setValue(data.getInt(KEY_VALUE));
+        }
+
+        @Override
+        public void onDestroy() {
+            super.onDestroy();
+            mOnDestroyListener.onEvent(this);
+        }
+
+        @Override
+        public void onSaveInstanceState(@NonNull Bundle outState) {
+            outState.putInt(KEY_VALUE, mValue);
+        }
+
+        public void setValue(int value) {
+            mValue = value;
+            TextView textView = getView().findViewById(R.id.text_view);
+            applyViewValue(textView, mValue);
+        }
+    }
+
+    private static void applyViewValue(TextView textView, int value) {
+        textView.setText(String.valueOf(value));
+        textView.setBackgroundColor(getColor(value));
+    }
+
+    private static int getColor(int value) {
+        return sColors[value % sColors.length];
+    }
+
+    @Test
+    public void fragmentAdapter_fullPass() throws Throwable {
+        testFragmentLifecycle(8, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0));
+    }
+
+    @Test
+    public void fragmentAdapter_random() throws Throwable {
+        final int totalPages = 10;
+        final int sequenceLength = 50;
+        testFragmentLifecycle_random(totalPages, sequenceLength, PageMutator.NO_OP);
+    }
+
+    @Test
+    public void fragmentAdapter_random_withMutations() throws Throwable {
+        final int totalPages = 10;
+        final int sequenceLength = 50;
+        testFragmentLifecycle_random(totalPages, sequenceLength, PageMutator.RANDOM);
+    }
+
+    private void testFragmentLifecycle_random(int totalPages, int sequenceLength,
+            PageMutator pageMutator) throws Throwable {
+        List<Integer> pageSequence = generateRandomPageSequence(totalPages, sequenceLength);
+
+        Log.i(getClass().getName(),
+                String.format("Testing with a sequence [%s]", TextUtils.join(", ", pageSequence)));
+
+        testFragmentLifecycle(totalPages, pageSequence, pageMutator);
+    }
+
+    @NonNull
+    private List<Integer> generateRandomPageSequence(int totalPages, int sequenceLength) {
+        List<Integer> pageSequence = new ArrayList<>(sequenceLength);
+
+        int pageIx = 0;
+        Double goRightProbability = null;
+        while (pageSequence.size() != sequenceLength) {
+            boolean goRight;
+            if (pageIx == 0) {
+                goRight = true;
+                goRightProbability = 0.7;
+            } else if (pageIx == totalPages - 1) { // last page
+                goRight = false;
+                goRightProbability = 0.3;
+            } else {
+                goRight = RANDOM.nextDouble() < goRightProbability;
+            }
+
+            pageSequence.add(goRight ? ++pageIx : --pageIx);
+        }
+
+        return pageSequence;
+    }
+
+    /**
+     * Test added when caught a bug: after the last swipe: actual=6, expected=4
+     * <p>
+     * Bug was caused by an invalid test assumption (new Fragment value can be inferred from number
+     * of instances created) - invalid in a case when we sometimes create Fragments off-screen and
+     * end up scrapping them.
+     **/
+    @Test
+    public void fragmentAdapter_regression1() throws Throwable {
+        testFragmentLifecycle(10, Arrays.asList(1, 2, 3, 2, 1, 2, 3, 4));
+    }
+
+    /**
+     * Test added when caught a bug: after the last swipe: actual=4, expected=5
+     * <p>
+     * Bug was caused by mSavedStates.add(position, ...) instead of mSavedStates.set(position, ...)
+     **/
+    @Test
+    public void fragmentAdapter_regression2() throws Throwable {
+        testFragmentLifecycle(10, Arrays.asList(1, 2, 3, 4, 3, 2, 1, 2, 3, 4, 5));
+    }
+
+    /**
+     * Test added when caught a bug: after the last swipe: ArrayIndexOutOfBoundsException: length=5;
+     * index=-1 at androidx.viewpager2.widget.tests.ViewPager2Tests$PageFragment.onCreateView
+     * <p>
+     * Bug was caused by always saving states of unattached fragments as null (even if there was a
+     * valid previously saved state)
+     */
+    @Test
+    public void fragmentAdapter_regression3() throws Throwable {
+        testFragmentLifecycle(10, Arrays.asList(1, 2, 3, 2, 1, 2, 3, 2, 1, 0));
+    }
+
+    /** Goes left on left edge / right on right edge */
+    @Test
+    public void fragmentAdapter_edges() throws Throwable {
+        testFragmentLifecycle(4, Arrays.asList(0, 0, 1, 2, 3, 3, 3, 2, 1, 0, 0, 0));
+    }
+
+    private interface PageMutator {
+        void mutate(PageFragment fragment);
+
+        PageMutator NO_OP = new PageMutator() {
+            @Override
+            public void mutate(PageFragment fragment) {
+                // do nothing
+            }
+        };
+
+        /** At random modifies the page under Fragment */
+        PageMutator RANDOM = new PageMutator() {
+            @Override
+            public void mutate(PageFragment fragment) {
+                Random random = ViewPager2Tests.RANDOM;
+                if (random.nextDouble() < 0.125) {
+                    int delta = (1 + random.nextInt(5)) * sColors.length;
+                    fragment.setValue(fragment.mValue + delta);
+                }
+            }
+        };
+    }
+
+    /** @see this#testFragmentLifecycle(int, List, PageMutator) */
+    private void testFragmentLifecycle(final int totalPages, List<Integer> pageSequence)
+            throws Throwable {
+        testFragmentLifecycle(totalPages, pageSequence, PageMutator.NO_OP);
+    }
+
+    /**
+     * Verifies:
+     * <ul>
+     * <li>page content / background
+     * <li>maximum number of Fragments held in memory
+     * <li>Fragment state saving / restoring
+     * </ul>
+     */
+    private void testFragmentLifecycle(final int totalPages, List<Integer> pageSequence,
+            final PageMutator pageMutator) throws Throwable {
+        final AtomicInteger attachCount = new AtomicInteger(0);
+        final AtomicInteger destroyCount = new AtomicInteger(0);
+        final boolean[] wasEverAttached = new boolean[totalPages];
+        final PageFragment[] fragments = new PageFragment[totalPages];
+
+        final int[] expectedValues = new int[totalPages];
+        for (int i = 0; i < totalPages; i++) {
+            expectedValues[i] = i;
+        }
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mViewPager.setAdapter(mActivityTestRule.getActivity().getSupportFragmentManager(),
+                        new ViewPager2.FragmentProvider() {
+                            @Override
+                            public Fragment getItem(final int position) {
+                                // if the fragment was attached in the past, it means we have
+                                // provided it with the correct value already; give a dummy one
+                                // to prove state save / restore functionality works
+                                int value = wasEverAttached[position] ? -1 : position;
+                                PageFragment fragment = PageFragment.create(position, value);
+
+                                fragment.mOnAttachListener = new PageFragment.EventListener() {
+                                    @Override
+                                    public void onEvent(PageFragment fragment) {
+                                        attachCount.incrementAndGet();
+                                        wasEverAttached[fragment.mPosition] = true;
+                                    }
+                                };
+
+                                fragment.mOnDestroyListener = new PageFragment.EventListener() {
+                                    @Override
+                                    public void onEvent(PageFragment fragment) {
+                                        destroyCount.incrementAndGet();
+                                    }
+                                };
+
+                                fragments[position] = fragment;
+                                return fragment;
+                            }
+
+                            @Override
+                            public int getCount() {
+                                return totalPages;
+                            }
+                        }, ViewPager2.FragmentRetentionPolicy.SAVE_STATE);
+            }
+        });
+
+        final AtomicInteger currentPage = new AtomicInteger(0);
+        verifyView(expectedValues[currentPage.get()]);
+        for (int nextPage : pageSequence) {
+            swipe(currentPage.get(), nextPage, totalPages);
+            currentPage.set(nextPage);
+            verifyView(expectedValues[currentPage.get()]);
+
+            // TODO: validate Fragments that are instantiated, but not attached. No destruction
+            // steps are done to them - they're just left to the Garbage Collector. Maybe
+            // WeakReferences could help, but the GC behaviour is not predictable. Alternatively,
+            // we could only create Fragments onAttach, but there is a potential performance
+            // trade-off.
+            assertThat(attachCount.get() - destroyCount.get(), isBetween(1, 4));
+
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    final int page = currentPage.get();
+                    PageFragment fragment = fragments[page];
+                    pageMutator.mutate(fragment);
+                    expectedValues[page] = fragment.mValue;
+                }
+            });
+        }
+    }
+
+    private void swipe(int currentPageIx, int nextPageIx, int totalPages)
+            throws InterruptedException {
+        if (nextPageIx >= totalPages) {
+            throw new IllegalArgumentException("Invalid nextPageIx: >= totalPages.");
+        }
+
+        if (currentPageIx == nextPageIx) { // dedicated for testing edge behaviour
+            if (nextPageIx == 0) {
+                swipeRight(); // bounce off the left edge
+                return;
+            }
+            if (nextPageIx == totalPages - 1) { // bounce off the right edge
+                swipeLeft();
+                return;
+            }
+            throw new IllegalArgumentException(
+                    "Invalid sequence. Not on an edge, and currentPageIx/nextPageIx pages same.");
+        }
+
+        if (Math.abs(nextPageIx - currentPageIx) > 1) {
+            throw new IllegalArgumentException(
+                    "Specified nextPageIx not adjacent to the current page.");
+        }
+
+        if (nextPageIx > currentPageIx) {
+            swipeLeft();
+        } else {
+            swipeRight();
+        }
+    }
+
+    private Matcher<Integer> isBetween(int min, int max) {
+        return allOf(greaterThanOrEqualTo(min), lessThanOrEqualTo(max));
+    }
+
+    @Test
+    public void viewAdapter_rendersAndHandlesSwiping() throws Throwable {
+        final int totalPages = 8;
+
+        if (Build.VERSION.SDK_INT < 16) { // TODO(b/71500143): remove temporary workaround
+            RecyclerView mRecyclerView = (RecyclerView) mViewPager.getChildAt(0);
+            mRecyclerView.setOverScrollMode(OVER_SCROLL_NEVER);
+        }
+
+        onView(withId(mViewPager.getId())).check(matches(isDisplayed()));
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mViewPager.setAdapter(
+                        new Adapter<ViewHolder>() {
+                            @NonNull
+                            @Override
+                            public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
+                                    int viewType) {
+                                return new ViewHolder(
+                                        mActivityTestRule.getActivity().getLayoutInflater().inflate(
+                                                R.layout.item_test_layout, parent, false)) {
+                                };
+                            }
+
+                            @Override
+                            public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+                                TextView view = (TextView) holder.itemView;
+                                applyViewValue(view, position);
+                            }
+
+                            @Override
+                            public int getItemCount() {
+                                return totalPages;
+                            }
+                        });
+            }
+        });
+
+        List<Integer> pageSequence = Arrays.asList(0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 6, 5, 4, 3, 2,
+                1, 0, 0, 0);
+        verifyView(0);
+        int currentPage = 0;
+        for (int nextPage : pageSequence) {
+            swipe(currentPage, nextPage, totalPages);
+            currentPage = nextPage;
+            verifyView(currentPage);
+        }
+    }
+
+    private void verifyView(int pageNumber) {
+        onView(allOf(withId(R.id.text_view), isDisplayed())).check(
+                matches(allOf(withText(String.valueOf(pageNumber)),
+                        new BackgroundColorMatcher(pageNumber))));
+    }
+
+    private static class BackgroundColorMatcher extends BaseMatcher<View> {
+        private final int mPageNumber;
+
+        BackgroundColorMatcher(int pageNumber) {
+            mPageNumber = pageNumber;
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText("should have background color: ").appendValue(
+                    getColor(mPageNumber));
+        }
+
+        @Override
+        public boolean matches(Object item) {
+            ColorDrawable background = (ColorDrawable) ((View) item).getBackground();
+            return background.getColor() == getColor(mPageNumber);
+        }
+    }
+
+    private void swipeLeft() throws InterruptedException {
+        performSwipe(ViewActions.swipeLeft());
+    }
+
+    private void swipeRight() throws InterruptedException {
+        performSwipe(ViewActions.swipeRight());
+    }
+
+    private void performSwipe(ViewAction swipeAction) throws InterruptedException {
+        mStableAfterSwipe = new CountDownLatch(1);
+        onView(allOf(isDisplayed(), withId(R.id.text_view))).perform(swipeAction);
+        mStableAfterSwipe.await(1, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void itemViewSizeMatchParentEnforced() {
+        mExpectedException.expect(IllegalStateException.class);
+        mExpectedException.expectMessage(
+                "Item's root view must fill the whole ViewPager2 (use match_parent)");
+
+        ViewPager2 viewPager = new ViewPager2(InstrumentationRegistry.getContext());
+        viewPager.setAdapter(new Adapter<ViewHolder>() {
+            @NonNull
+            @Override
+            public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+                View view = new View(parent.getContext());
+                view.setLayoutParams(new ViewGroup.LayoutParams(50, 50)); // arbitrary fixed size
+                return new ViewHolder(view) {
+                };
+            }
+
+            @Override
+            public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+                // do nothing
+            }
+
+            @Override
+            public int getItemCount() {
+                return 1;
+            }
+        });
+
+        viewPager.measure(0, 0); // equivalent of unspecified
+    }
+
+    @Test
+    public void childrenNotAllowed() throws Exception {
+        mExpectedException.expect(IllegalStateException.class);
+        mExpectedException.expectMessage("ViewPager2 does not support direct child views");
+
+        Context context = InstrumentationRegistry.getContext();
+        ViewPager2 viewPager = new ViewPager2(context);
+        viewPager.addView(new View(context));
+    }
+
+    // TODO: verify correct padding behavior
+    // TODO: add test for screen orientation change
+    // TODO: port some of the fragment adapter tests as view adapter tests
+}
diff --git a/viewpager2/src/androidTest/java/androidx/widget/viewpager2/tests/ViewPager2Tests.java b/viewpager2/src/androidTest/java/androidx/widget/viewpager2/tests/ViewPager2Tests.java
deleted file mode 100644
index 45b42aa..0000000
--- a/viewpager2/src/androidTest/java/androidx/widget/viewpager2/tests/ViewPager2Tests.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (C) 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 androidx.widget.viewpager2.tests;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
-import static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
-import static android.support.v7.widget.RecyclerView.SCROLL_STATE_SETTLING;
-import static android.view.View.OVER_SCROLL_NEVER;
-
-import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.espresso.IdlingRegistry;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.ViewActions;
-import android.support.test.espresso.idling.CountingIdlingResource;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.Adapter;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-
-import androidx.widget.ViewPager2;
-import androidx.widget.viewpager2.test.R;
-
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class ViewPager2Tests {
-    private static final int[] sColors = {
-            Color.parseColor("#BBA9FF00"),
-            Color.parseColor("#BB00E87E"),
-            Color.parseColor("#BB00C7FF"),
-            Color.parseColor("#BBB30CE8"),
-            Color.parseColor("#BBFF00D0")};
-
-    @Rule
-    public final ActivityTestRule<TestActivity> mActivityTestRule;
-    @Rule
-    public ExpectedException mExpectedException = ExpectedException.none();
-
-    private ViewPager2 mViewPager;
-    private CountingIdlingResource mIdlingResource;
-
-    public ViewPager2Tests() {
-        mActivityTestRule = new ActivityTestRule<>(TestActivity.class);
-    }
-
-    @Before
-    public void setUp() {
-        mViewPager = mActivityTestRule.getActivity().findViewById(R.id.view_pager);
-
-        mIdlingResource = new CountingIdlingResource(getClass().getSimpleName() + "IdlingResource");
-        mViewPager.addOnScrollListener(new RecyclerView.OnScrollListener() {
-            @Override
-            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
-                if (newState == SCROLL_STATE_IDLE && !mIdlingResource.isIdleNow()) {
-                    mIdlingResource.decrement();
-                } else if (newState == SCROLL_STATE_SETTLING && mIdlingResource.isIdleNow()) {
-                    mIdlingResource.increment();
-                }
-            }
-        });
-        IdlingRegistry.getInstance().register(mIdlingResource);
-    }
-
-    @After
-    public void tearDown() {
-        IdlingRegistry.getInstance().unregister(mIdlingResource);
-    }
-
-    @Test
-    public void rendersAndHandlesSwiping() throws Throwable {
-        final int pageCount = sColors.length;
-
-        if (Build.VERSION.SDK_INT < 16) { // TODO(b/71500143): remove temporary workaround
-            RecyclerView mRecyclerView = (RecyclerView) mViewPager.getChildAt(0);
-            mRecyclerView.setOverScrollMode(OVER_SCROLL_NEVER);
-        }
-
-        onView(withId(mViewPager.getId())).check(matches(isDisplayed()));
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mViewPager.setAdapter(
-                        new Adapter<ViewHolder>() {
-                            @NonNull
-                            @Override
-                            public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
-                                    int viewType) {
-                                return new ViewHolder(
-                                        mActivityTestRule.getActivity().getLayoutInflater().inflate(
-                                                R.layout.item_test_layout, parent, false)) {
-                                };
-                            }
-
-                            @Override
-                            public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
-                                TextView view = (TextView) holder.itemView;
-                                view.setText(String.valueOf(position));
-                                view.setBackgroundColor(sColors[position]);
-                            }
-
-                            @Override
-                            public int getItemCount() {
-                                return pageCount;
-                            }
-                        });
-            }
-        });
-
-        final int pageIxFirst = 0;
-        final int pageIxLast = pageCount - 1;
-        final int swipeCount = pageCount + 1; // two swipes beyond edge to test 'edge behavior'
-        int pageNumber = pageIxFirst;
-        for (int i = 0; i < swipeCount; i++, pageNumber = Math.min(pageIxLast, ++pageNumber)) {
-            verifyView(pageNumber);
-            performSwipe(ViewActions.swipeLeft());
-        }
-        assertThat(pageNumber, equalTo(pageIxLast));
-        for (int i = 0; i < swipeCount; i++, pageNumber = Math.max(pageIxFirst, --pageNumber)) {
-            verifyView(pageNumber);
-            performSwipe(ViewActions.swipeRight());
-        }
-        assertThat(pageNumber, equalTo(pageIxFirst));
-    }
-
-    private void verifyView(int pageNumber) {
-        onView(allOf(withId(R.id.text_view), isDisplayed())).check(
-                matches(withText(String.valueOf(pageNumber))));
-    }
-
-    private void performSwipe(ViewAction swipeAction) throws InterruptedException {
-        onView(allOf(isDisplayed(), withId(R.id.text_view))).perform(swipeAction);
-    }
-
-    @Test
-    public void itemViewSizeMatchParentEnforced() {
-        mExpectedException.expect(IllegalStateException.class);
-        mExpectedException.expectMessage(
-                "Item's root view must fill the whole ViewPager2 (use match_parent)");
-
-        ViewPager2 viewPager = new ViewPager2(InstrumentationRegistry.getContext());
-        viewPager.setAdapter(new Adapter<ViewHolder>() {
-            @NonNull
-            @Override
-            public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-                View view = new View(parent.getContext());
-                view.setLayoutParams(new ViewGroup.LayoutParams(50, 50)); // arbitrary fixed size
-                return new ViewHolder(view) {
-                };
-            }
-
-            @Override
-            public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
-                // do nothing
-            }
-
-            @Override
-            public int getItemCount() {
-                return 1;
-            }
-        });
-
-        viewPager.measure(0, 0); // equivalent of unspecified
-    }
-
-    @Test
-    public void childrenNotAllowed() throws Exception {
-        mExpectedException.expect(IllegalStateException.class);
-        mExpectedException.expectMessage("ViewPager2 does not support direct child views");
-
-        Context context = InstrumentationRegistry.getContext();
-        ViewPager2 viewPager = new ViewPager2(context);
-        viewPager.addView(new View(context));
-    }
-
-    // TODO: verify correct padding behavior
-    // TODO: add test for screen orientation change
-}
diff --git a/viewpager2/src/androidTest/res/layout/activity_test_layout.xml b/viewpager2/src/androidTest/res/layout/activity_test_layout.xml
index 3037029..9d996ab 100644
--- a/viewpager2/src/androidTest/res/layout/activity_test_layout.xml
+++ b/viewpager2/src/androidTest/res/layout/activity_test_layout.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<androidx.widget.ViewPager2
+<androidx.viewpager2.widget.ViewPager2
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/view_pager"
     android:layout_width="match_parent"
diff --git a/viewpager2/src/main/AndroidManifest.xml b/viewpager2/src/main/AndroidManifest.xml
index ebddd6c..cc03d91 100644
--- a/viewpager2/src/main/AndroidManifest.xml
+++ b/viewpager2/src/main/AndroidManifest.xml
@@ -14,4 +14,4 @@
      limitations under the License.
 -->
 
-<manifest package="androidx.widget.viewpager2"/>
\ No newline at end of file
+<manifest package="androidx.viewpager2"/>
\ No newline at end of file
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
new file mode 100644
index 0000000..a5d8759
--- /dev/null
+++ b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 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 androidx.viewpager2.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.view.ViewCompat;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.PagerSnapHelper;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.Adapter;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import java.lang.annotation.Retention;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Work in progress: go/viewpager2
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class ViewPager2 extends ViewGroup {
+    // reused in layout(...)
+    private final Rect mTmpContainerRect = new Rect();
+    private final Rect mTmpChildRect = new Rect();
+
+    private RecyclerView mRecyclerView;
+
+    public ViewPager2(Context context) {
+        super(context);
+        initialize(context);
+    }
+
+    public ViewPager2(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ViewPager2(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initialize(context);
+    }
+
+    @RequiresApi(21)
+    public ViewPager2(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        // TODO(b/70663531): handle attrs, defStyleAttr, defStyleRes
+        super(context, attrs, defStyleAttr, defStyleRes);
+        initialize(context);
+    }
+
+    private void initialize(Context context) {
+        mRecyclerView = new RecyclerView(context);
+
+        LinearLayoutManager layoutManager = new LinearLayoutManager(context);
+        // TODO(b/69103581): add support for vertical layout
+        // TODO(b/69398856): add support for RTL
+        layoutManager.setOrientation(RecyclerView.HORIZONTAL);
+        mRecyclerView.setLayoutManager(layoutManager);
+
+        mRecyclerView.setLayoutParams(
+                new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+
+        // TODO(b/70666992): add automated test for orientation change
+        new PagerSnapHelper().attachToRecyclerView(mRecyclerView);
+
+        attachViewToParent(mRecyclerView, 0, mRecyclerView.getLayoutParams());
+    }
+
+    /**
+     * TODO(b/70663708): decide on an Adapter class. Here supporting RecyclerView.Adapter.
+     *
+     * @see RecyclerView#setAdapter(Adapter)
+     */
+    public <VH extends ViewHolder> void setAdapter(final Adapter<VH> adapter) {
+        mRecyclerView.setAdapter(new Adapter<VH>() {
+            private final Adapter<VH> mAdapter = adapter;
+
+            @NonNull
+            @Override
+            public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+                VH viewHolder = mAdapter.onCreateViewHolder(parent, viewType);
+
+                LayoutParams layoutParams = viewHolder.itemView.getLayoutParams();
+                if ((layoutParams.width | layoutParams.height) != LayoutParams.MATCH_PARENT) {
+                    // TODO(b/70666614): decide if throw an exception or wrap in FrameLayout
+                    // ourselves; consider accepting exact size equal to parent's exact size
+                    throw new IllegalStateException(String.format(
+                            "Item's root view must fill the whole %s (use match_parent)",
+                            ViewPager2.this.getClass().getSimpleName()));
+                }
+
+                return viewHolder;
+            }
+
+            @Override
+            public void onBindViewHolder(@NonNull VH holder, int position) {
+                mAdapter.onBindViewHolder(holder, position);
+            }
+
+            @Override
+            public int getItemCount() {
+                return mAdapter.getItemCount();
+            }
+        });
+    }
+
+    /**
+     * TODO(b/70663708): decide on an Adapter class. Here supporting {@link Fragment}s.
+     *
+     * @param fragmentRetentionPolicy allows for future parameterization of Fragment memory
+     *                                strategy, similar to what {@link FragmentPagerAdapter} and
+     *                                {@link FragmentStatePagerAdapter} provide.
+     */
+    public void setAdapter(FragmentManager fragmentManager, FragmentProvider fragmentProvider,
+            @FragmentRetentionPolicy int fragmentRetentionPolicy) {
+        if (fragmentRetentionPolicy != FragmentRetentionPolicy.SAVE_STATE) {
+            throw new IllegalArgumentException("Currently only SAVE_STATE policy is supported");
+        }
+
+        mRecyclerView.setAdapter(new FragmentStateAdapter(fragmentManager, fragmentProvider));
+    }
+
+    /**
+     * Similar in behavior to {@link FragmentStatePagerAdapter}
+     * <p>
+     * Lifecycle within {@link RecyclerView}:
+     * <ul>
+     * <li>{@link RecyclerView.ViewHolder} initially an empty {@link FrameLayout}, serves as a
+     * re-usable container for a {@link Fragment} in later stages.
+     * <li>{@link RecyclerView.Adapter#onBindViewHolder} we ask for a {@link Fragment} for the
+     * position. If we already have the fragment, or have previously saved its state, we use those.
+     * <li>{@link RecyclerView.Adapter#onAttachedToWindow} we attach the {@link Fragment} to a
+     * container.
+     * <li>{@link RecyclerView.Adapter#onViewRecycled} and
+     * {@link RecyclerView.Adapter#onFailedToRecycleView} we remove, save state, destroy the
+     * {@link Fragment}.
+     * </ul>
+     */
+    private static class FragmentStateAdapter extends RecyclerView.Adapter<FragmentViewHolder> {
+        private final List<Fragment.SavedState> mSavedStates = new ArrayList<>();
+        // TODO: handle current item's menuVisibility userVisibleHint as FragmentStatePagerAdapter
+
+        private final FragmentManager mFragmentManager;
+        private final FragmentProvider mFragmentProvider;
+
+        private FragmentStateAdapter(FragmentManager fragmentManager,
+                FragmentProvider fragmentProvider) {
+            this.mFragmentManager = fragmentManager;
+            this.mFragmentProvider = fragmentProvider;
+        }
+
+        @NonNull
+        @Override
+        public FragmentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+            return FragmentViewHolder.create(parent);
+        }
+
+        @Override
+        public void onBindViewHolder(@NonNull FragmentViewHolder holder, int position) {
+            if (ViewCompat.isAttachedToWindow(holder.getContainer())) {
+                // this should never happen; if it does, it breaks our assumption that attaching
+                // a Fragment can reliably happen inside onViewAttachedToWindow
+                throw new IllegalStateException(
+                        String.format("View %s unexpectedly attached to a window.",
+                                holder.getContainer()));
+            }
+
+            holder.mFragment = getFragment(position);
+        }
+
+        private Fragment getFragment(int position) {
+            Fragment fragment = mFragmentProvider.getItem(position);
+            if (mSavedStates.size() > position) {
+                Fragment.SavedState savedState = mSavedStates.get(position);
+                if (savedState != null) {
+                    fragment.setInitialSavedState(savedState);
+                }
+            }
+            return fragment;
+        }
+
+        @Override
+        public void onViewAttachedToWindow(@NonNull FragmentViewHolder holder) {
+            if (holder.mFragment.isAdded()) {
+                return;
+            }
+            mFragmentManager.beginTransaction().add(holder.getContainer().getId(),
+                    holder.mFragment).commitNowAllowingStateLoss();
+        }
+
+        @Override
+        public int getItemCount() {
+            return mFragmentProvider.getCount();
+        }
+
+        @Override
+        public void onViewRecycled(@NonNull FragmentViewHolder holder) {
+            removeFragment(holder);
+        }
+
+        @Override
+        public boolean onFailedToRecycleView(@NonNull FragmentViewHolder holder) {
+            // This happens when a ViewHolder is in a transient state (e.g. during custom
+            // animation). We don't have sufficient information on how to clear up what lead to
+            // the transient state, so we are throwing away the ViewHolder to stay on the
+            // conservative side.
+            removeFragment(holder);
+            return false; // don't recycle the view
+        }
+
+        private void removeFragment(@NonNull FragmentViewHolder holder) {
+            if (holder.mFragment == null) {
+                return; // fresh ViewHolder, nothing to do
+            }
+
+            int position = holder.getAdapterPosition();
+
+            if (holder.mFragment.isAdded()) {
+                while (mSavedStates.size() <= position) {
+                    mSavedStates.add(null);
+                }
+                mSavedStates.set(position,
+                        mFragmentManager.saveFragmentInstanceState(holder.mFragment));
+            }
+
+            mFragmentManager.beginTransaction().remove(
+                    holder.mFragment).commitNowAllowingStateLoss();
+            holder.mFragment = null;
+        }
+    }
+
+    private static class FragmentViewHolder extends RecyclerView.ViewHolder {
+        private Fragment mFragment;
+
+        private FragmentViewHolder(FrameLayout container) {
+            super(container);
+        }
+
+        static FragmentViewHolder create(ViewGroup parent) {
+            FrameLayout container = new FrameLayout(parent.getContext());
+            container.setLayoutParams(
+                    new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                            ViewGroup.LayoutParams.MATCH_PARENT));
+            container.setId(ViewCompat.generateViewId());
+            return new FragmentViewHolder(container);
+        }
+
+        FrameLayout getContainer() {
+            return (FrameLayout) itemView;
+        }
+    }
+
+    /**
+     * Provides {@link Fragment}s for pages
+     */
+    public interface FragmentProvider {
+        /**
+         * Return the Fragment associated with a specified position.
+         */
+        Fragment getItem(int position);
+
+        /**
+         * Return the number of pages available.
+         */
+        int getCount();
+    }
+
+    @Retention(CLASS)
+    @IntDef({FragmentRetentionPolicy.SAVE_STATE})
+    public @interface FragmentRetentionPolicy {
+        /** Approach similar to {@link FragmentStatePagerAdapter} */
+        int SAVE_STATE = 0;
+    }
+
+    @Override
+    public void onViewAdded(View child) {
+        // TODO(b/70666620): consider adding a support for Decor views
+        throw new IllegalStateException(
+                getClass().getSimpleName() + " does not support direct child views");
+    }
+
+    /** @see RecyclerView#addOnScrollListener(RecyclerView.OnScrollListener) */
+    public void addOnScrollListener(RecyclerView.OnScrollListener listener) {
+        mRecyclerView.addOnScrollListener(listener);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // TODO(b/70666622): consider margin support
+        // TODO(b/70666626): consider delegating all this to RecyclerView
+        // TODO(b/70666625): write automated tests for this
+
+        measureChild(mRecyclerView, widthMeasureSpec, heightMeasureSpec);
+        int width = mRecyclerView.getMeasuredWidth();
+        int height = mRecyclerView.getMeasuredHeight();
+        int childState = mRecyclerView.getMeasuredState();
+
+        width += getPaddingLeft() + getPaddingRight();
+        height += getPaddingTop() + getPaddingBottom();
+
+        width = Math.max(width, getSuggestedMinimumWidth());
+        height = Math.max(height, getSuggestedMinimumHeight());
+
+        setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState),
+                resolveSizeAndState(height, heightMeasureSpec,
+                        childState << MEASURED_HEIGHT_STATE_SHIFT));
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        int width = mRecyclerView.getMeasuredWidth();
+        int height = mRecyclerView.getMeasuredHeight();
+
+        // TODO(b/70666626): consider delegating padding handling to the RecyclerView to avoid
+        // an unnatural page transition effect: http://shortn/_Vnug3yZpQT
+        mTmpContainerRect.left = getPaddingLeft();
+        mTmpContainerRect.right = r - l - getPaddingRight();
+        mTmpContainerRect.top = getPaddingTop();
+        mTmpContainerRect.bottom = b - t - getPaddingBottom();
+
+        Gravity.apply(Gravity.TOP | Gravity.START, width, height, mTmpContainerRect, mTmpChildRect);
+        mRecyclerView.layout(mTmpChildRect.left, mTmpChildRect.top, mTmpChildRect.right,
+                mTmpChildRect.bottom);
+    }
+}
diff --git a/viewpager2/src/main/java/androidx/widget/ViewPager2.java b/viewpager2/src/main/java/androidx/widget/ViewPager2.java
deleted file mode 100644
index 9ebdea1..0000000
--- a/viewpager2/src/main/java/androidx/widget/ViewPager2.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 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 androidx.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.PagerSnapHelper;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.Adapter;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Work in progress: go/viewpager2
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ViewPager2 extends ViewGroup {
-    // reused in layout(...)
-    private final Rect mTmpContainerRect = new Rect();
-    private final Rect mTmpChildRect = new Rect();
-
-    private RecyclerView mRecyclerView;
-
-    public ViewPager2(Context context) {
-        super(context);
-        initialize(context);
-    }
-
-    public ViewPager2(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public ViewPager2(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        initialize(context);
-    }
-
-    @RequiresApi(21)
-    public ViewPager2(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        // TODO(b/70663531): handle attrs, defStyleAttr, defStyleRes
-        super(context, attrs, defStyleAttr, defStyleRes);
-        initialize(context);
-    }
-
-    private void initialize(Context context) {
-        mRecyclerView = new RecyclerView(context);
-
-        LinearLayoutManager layoutManager = new LinearLayoutManager(context);
-        // TODO(b/69103581): add support for vertical layout
-        // TODO(b/69398856): add support for RTL
-        layoutManager.setOrientation(RecyclerView.HORIZONTAL);
-        mRecyclerView.setLayoutManager(layoutManager);
-
-        mRecyclerView.setLayoutParams(
-                new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
-
-        // TODO(b/70666992): add automated test for orientation change
-        new PagerSnapHelper().attachToRecyclerView(mRecyclerView);
-
-        attachViewToParent(mRecyclerView, 0, mRecyclerView.getLayoutParams());
-    }
-
-    /**
-     * TODO(b/70663708): decide on an Adapter class (for now reusing RecyclerView.Adapter)
-     *
-     * @see RecyclerView#setAdapter(Adapter)
-     */
-    public <VH extends ViewHolder> void setAdapter(final Adapter<VH> adapter) {
-        mRecyclerView.setAdapter(new Adapter<VH>() {
-            private final Adapter<VH> mAdapter = adapter;
-
-            @NonNull
-            @Override
-            public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-                VH viewHolder = mAdapter.onCreateViewHolder(parent, viewType);
-
-                LayoutParams layoutParams = viewHolder.itemView.getLayoutParams();
-                if ((layoutParams.width | layoutParams.height) != LayoutParams.MATCH_PARENT) {
-                    // TODO(b/70666614): decide if throw an exception or wrap in FrameLayout
-                    // ourselves; consider accepting exact size equal to parent's exact size
-                    throw new IllegalStateException(String.format(
-                            "Item's root view must fill the whole %s (use match_parent)",
-                            ViewPager2.this.getClass().getSimpleName()));
-                }
-
-                return viewHolder;
-            }
-
-            @Override
-            public void onBindViewHolder(@NonNull VH holder, int position) {
-                mAdapter.onBindViewHolder(holder, position);
-            }
-
-            @Override
-            public int getItemCount() {
-                return mAdapter.getItemCount();
-            }
-        });
-    }
-
-    @Override
-    public void onViewAdded(View child) {
-        // TODO(b/70666620): consider adding a support for Decor views
-        throw new IllegalStateException(
-                getClass().getSimpleName() + " does not support direct child views");
-    }
-
-    /** @see RecyclerView#addOnScrollListener(RecyclerView.OnScrollListener) */
-    public void addOnScrollListener(RecyclerView.OnScrollListener listener) {
-        mRecyclerView.addOnScrollListener(listener);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // TODO(b/70666622): consider margin support
-        // TODO(b/70666626): consider delegating all this to RecyclerView
-        // TODO(b/70666625): write automated tests for this
-
-        measureChild(mRecyclerView, widthMeasureSpec, heightMeasureSpec);
-        int width = mRecyclerView.getMeasuredWidth();
-        int height = mRecyclerView.getMeasuredHeight();
-        int childState = mRecyclerView.getMeasuredState();
-
-        width += getPaddingLeft() + getPaddingRight();
-        height += getPaddingTop() + getPaddingBottom();
-
-        width = Math.max(width, getSuggestedMinimumWidth());
-        height = Math.max(height, getSuggestedMinimumHeight());
-
-        setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState),
-                resolveSizeAndState(height, heightMeasureSpec,
-                        childState << MEASURED_HEIGHT_STATE_SHIFT));
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        int width = mRecyclerView.getMeasuredWidth();
-        int height = mRecyclerView.getMeasuredHeight();
-
-        // TODO(b/70666626): consider delegating padding handling to the RecyclerView to avoid
-        // an unnatural page transition effect: http://shortn/_Vnug3yZpQT
-        mTmpContainerRect.left = getPaddingLeft();
-        mTmpContainerRect.right = r - l - getPaddingRight();
-        mTmpContainerRect.top = getPaddingTop();
-        mTmpContainerRect.bottom = b - t - getPaddingBottom();
-
-        Gravity.apply(Gravity.TOP | Gravity.START, width, height, mTmpContainerRect, mTmpChildRect);
-        mRecyclerView.layout(mTmpChildRect.left, mTmpChildRect.top, mTmpChildRect.right,
-                mTmpChildRect.bottom);
-    }
-}
diff --git a/wear/build.gradle b/wear/build.gradle
index 2b832e7..ffc7990 100644
--- a/wear/build.gradle
+++ b/wear/build.gradle
@@ -1,6 +1,6 @@
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/webkit-codegen/build.gradle b/webkit-codegen/build.gradle
index 92090e1..891382b 100644
--- a/webkit-codegen/build.gradle
+++ b/webkit-codegen/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
+import static androidx.build.dependencies.DependenciesKt.*
 
 apply plugin: 'maven'
 apply plugin: 'application'
diff --git a/webkit/api/current.txt b/webkit/api/current.txt
index b0d66d6..7a3fe25 100644
--- a/webkit/api/current.txt
+++ b/webkit/api/current.txt
@@ -1,5 +1,14 @@
 package androidx.webkit {
 
+  public class WebSettingsCompat {
+    method public static int getDisabledActionModeMenuItems(android.webkit.WebSettings);
+    method public static boolean getOffscreenPreRaster(android.webkit.WebSettings);
+    method public static boolean getSafeBrowsingEnabled(android.webkit.WebSettings);
+    method public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
+    method public static void setOffscreenPreRaster(android.webkit.WebSettings, boolean);
+    method public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
+  }
+
   public class WebViewCompat {
     method public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
   }
diff --git a/webkit/build.gradle b/webkit/build.gradle
index 7975fc8..7d317f1 100644
--- a/webkit/build.gradle
+++ b/webkit/build.gradle
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-import static android.support.dependencies.DependenciesKt.*
-import android.support.LibraryGroups
-import android.support.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
 
 plugins {
     id("SupportAndroidLibraryPlugin")
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java b/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java
new file mode 100644
index 0000000..1e5c152
--- /dev/null
+++ b/webkit/src/androidTest/java/androidx/webkit/WebSettingsCompatTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.webkit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Build;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.webkit.WebSettings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class WebSettingsCompatTest {
+    WebViewOnUiThread mWebViewOnUiThread;
+
+    @Before
+    public void setUp() {
+        mWebViewOnUiThread = new androidx.webkit.WebViewOnUiThread();
+    }
+
+    @Test
+    public void testOffscreenPreRaster() {
+        // TODO(gsennton) activate this test for pre-M devices when we can pre-install a WebView APK
+        // containing support for the WebView Support Library, see b/73454652.
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
+
+        assertFalse(WebSettingsCompat.getOffscreenPreRaster(mWebViewOnUiThread.getSettings()));
+
+        WebSettingsCompat.setOffscreenPreRaster(mWebViewOnUiThread.getSettings(), true);
+        assertTrue(WebSettingsCompat.getOffscreenPreRaster(mWebViewOnUiThread.getSettings()));
+    }
+
+    @Test
+    public void testEnableSafeBrowsing() throws Throwable {
+        // TODO(gsennton) activate this test for old devices when we can pre-install a WebView APK
+        // containing support for the WebView Support Library, see b/73454652.
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
+
+        WebSettingsCompat.setSafeBrowsingEnabled(mWebViewOnUiThread.getSettings(), false);
+        assertFalse(WebSettingsCompat.getSafeBrowsingEnabled(mWebViewOnUiThread.getSettings()));
+    }
+
+    @Test
+    public void testDisabledActionModeMenuItems() throws Throwable {
+        // TODO(gsennton) activate this test for old devices when we can pre-install a WebView APK
+        // containing support for the WebView Support Library, see b/73454652.
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return;
+
+        assertEquals(WebSettings.MENU_ITEM_NONE,
+                WebSettingsCompat.getDisabledActionModeMenuItems(mWebViewOnUiThread.getSettings()));
+
+        WebSettingsCompat.setDisabledActionModeMenuItems(mWebViewOnUiThread.getSettings(),
+                WebSettings.MENU_ITEM_SHARE);
+        assertEquals(WebSettings.MENU_ITEM_SHARE,
+                WebSettingsCompat.getDisabledActionModeMenuItems(mWebViewOnUiThread.getSettings()));
+
+        WebSettingsCompat.setDisabledActionModeMenuItems(mWebViewOnUiThread.getSettings(),
+                WebSettings.MENU_ITEM_PROCESS_TEXT | WebSettings.MENU_ITEM_WEB_SEARCH);
+        assertEquals(WebSettings.MENU_ITEM_PROCESS_TEXT | WebSettings.MENU_ITEM_WEB_SEARCH,
+                WebSettingsCompat.getDisabledActionModeMenuItems(mWebViewOnUiThread.getSettings()));
+    }
+}
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebViewOnUiThread.java b/webkit/src/androidTest/java/androidx/webkit/WebViewOnUiThread.java
index 6219bd3..a86775b 100644
--- a/webkit/src/androidTest/java/androidx/webkit/WebViewOnUiThread.java
+++ b/webkit/src/androidTest/java/androidx/webkit/WebViewOnUiThread.java
@@ -17,6 +17,7 @@
 package androidx.webkit;
 
 import android.support.test.InstrumentationRegistry;
+import android.webkit.WebSettings;
 import android.webkit.WebView;
 
 public class WebViewOnUiThread {
@@ -50,7 +51,36 @@
         });
     }
 
+    public WebSettings getSettings() {
+        return getValue(new ValueGetter<WebSettings>() {
+            @Override
+            public WebSettings capture() {
+                return mWebView.getSettings();
+            }
+        });
+    }
+
     public WebView getWebViewOnCurrentThread() {
         return mWebView;
     }
+
+    private <T> T getValue(ValueGetter<T> getter) {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(getter);
+        return getter.getValue();
+    }
+
+    private abstract class ValueGetter<T> implements Runnable {
+        private T mValue;
+
+        @Override
+        public void run() {
+            mValue = capture();
+        }
+
+        protected abstract T capture();
+
+        public T getValue() {
+            return mValue;
+        }
+    }
 }
diff --git a/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java b/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
new file mode 100644
index 0000000..c73cda6
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.webkit;
+
+import android.os.Build;
+import android.webkit.WebSettings;
+
+import androidx.webkit.internal.WebSettingsAdapter;
+import androidx.webkit.internal.WebViewGlueCommunicator;
+
+/**
+ * Compatibility version of {@link android.webkit.WebSettings}
+ */
+public class WebSettingsCompat {
+    private WebSettingsCompat() {}
+
+    // TODO(gsennton): add feature detection
+
+    /**
+     * Sets whether this WebView should raster tiles when it is
+     * offscreen but attached to a window. Turning this on can avoid
+     * rendering artifacts when animating an offscreen WebView on-screen.
+     * Offscreen WebViews in this mode use more memory. The default value is
+     * false.<br>
+     * Please follow these guidelines to limit memory usage:
+     * <ul>
+     * <li> WebView size should be not be larger than the device screen size.
+     * <li> Limit use of this mode to a small number of WebViews. Use it for
+     *   visible WebViews and WebViews about to be animated to visible.
+     * </ul>
+     */
+    public static void setOffscreenPreRaster(WebSettings webSettings, boolean enabled) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            webSettings.setOffscreenPreRaster(enabled);
+        } else {
+            getAdapter(webSettings).setOffscreenPreRaster(enabled);
+        }
+    }
+
+    /**
+     * Gets whether this WebView should raster tiles when it is
+     * offscreen but attached to a window.
+     * @return {@code true} if this WebView will raster tiles when it is
+     * offscreen but attached to a window.
+     */
+    public static boolean getOffscreenPreRaster(WebSettings webSettings) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            return webSettings.getOffscreenPreRaster();
+        } else {
+            return getAdapter(webSettings).getOffscreenPreRaster();
+        }
+    }
+
+    /**
+     * Sets whether Safe Browsing is enabled. Safe Browsing allows WebView to
+     * protect against malware and phishing attacks by verifying the links.
+     *
+     * <p>
+     * Safe Browsing can be disabled for all WebViews using a manifest tag (read <a
+     * href="{@docRoot}reference/android/webkit/WebView.html">general Safe Browsing info</a>). The
+     * manifest tag has a lower precedence than this API.
+     *
+     * <p>
+     * Safe Browsing is enabled by default for devices which support it.
+     *
+     * @param enabled Whether Safe Browsing is enabled.
+     */
+    public static void setSafeBrowsingEnabled(WebSettings webSettings, boolean enabled) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            webSettings.setSafeBrowsingEnabled(enabled);
+        } else {
+            getAdapter(webSettings).setSafeBrowsingEnabled(enabled);
+        }
+    }
+
+    /**
+     * Gets whether Safe Browsing is enabled.
+     * See {@link #setSafeBrowsingEnabled}.
+     *
+     * @return {@code true} if Safe Browsing is enabled and {@code false} otherwise.
+     */
+    public static boolean getSafeBrowsingEnabled(WebSettings webSettings) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            return webSettings.getSafeBrowsingEnabled();
+        } else {
+            return getAdapter(webSettings).getSafeBrowsingEnabled();
+        }
+    }
+
+    /**
+     * Disables the action mode menu items according to {@code menuItems} flag.
+     * @param menuItems an integer field flag for the menu items to be disabled.
+     */
+    public static void setDisabledActionModeMenuItems(WebSettings webSettings, int menuItems) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            webSettings.setDisabledActionModeMenuItems(menuItems);
+        } else {
+            getAdapter(webSettings).setDisabledActionModeMenuItems(menuItems);
+        }
+    }
+
+    /**
+     * Gets the action mode menu items that are disabled, expressed in an integer field flag.
+     * The default value is {@link WebSettings#MENU_ITEM_NONE}
+     *
+     * @return all the disabled menu item flags combined with bitwise OR.
+     */
+    public static int getDisabledActionModeMenuItems(WebSettings webSettings) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            return webSettings.getDisabledActionModeMenuItems();
+        } else {
+            return getAdapter(webSettings).getDisabledActionModeMenuItems();
+        }
+    }
+
+    private static WebSettingsAdapter getAdapter(WebSettings webSettings) {
+        return WebViewGlueCommunicator.getCompatConverter().convertSettings(webSettings);
+    }
+}
+
diff --git a/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java b/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java
new file mode 100644
index 0000000..091879c
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/internal/WebSettingsAdapter.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.webkit.internal;
+
+import org.chromium.support_lib_boundary.WebSettingsBoundaryInterface;
+
+/**
+ * Adapter between WebSettingsCompat and
+ * {@link org.chromium.support_lib_boundary.WebSettingsBoundaryInterface} (the
+ * corresponding interface shared with the support library glue in the WebView APK).
+ */
+public class WebSettingsAdapter {
+    private WebSettingsBoundaryInterface mBoundaryInterface;
+
+    public WebSettingsAdapter(WebSettingsBoundaryInterface boundaryInterface) {
+        mBoundaryInterface = boundaryInterface;
+    }
+
+    /**
+     * Adapter method for {@link androidx.webkit.WebSettingsCompat#setOffscreenPreRaster}.
+     */
+    public void setOffscreenPreRaster(boolean enabled) {
+        mBoundaryInterface.setOffscreenPreRaster(enabled);
+    }
+
+    /**
+     * Adapter method for {@link androidx.webkit.WebSettingsCompat#getOffscreenPreRaster}.
+     */
+    public boolean getOffscreenPreRaster() {
+        return mBoundaryInterface.getOffscreenPreRaster();
+    }
+
+    /**
+     * Adapter method for {@link androidx.webkit.WebSettingsCompat#setSafeBrowsingEnabled}.
+     */
+    public void setSafeBrowsingEnabled(boolean enabled) {
+        mBoundaryInterface.setSafeBrowsingEnabled(enabled);
+    }
+
+    /**
+     * Adapter method for {@link androidx.webkit.WebSettingsCompat#getSafeBrowsingEnabled}.
+     */
+    public boolean getSafeBrowsingEnabled() {
+        return mBoundaryInterface.getSafeBrowsingEnabled();
+    }
+
+    /**
+     * Adapter method for {@link androidx.webkit.WebSettingsCompat#setDisabledActionModeMenuItems}.
+     */
+    public void setDisabledActionModeMenuItems(int menuItems) {
+        mBoundaryInterface.setDisabledActionModeMenuItems(menuItems);
+    }
+
+    /**
+     * Adapter method for {@link androidx.webkit.WebSettingsCompat#getDisabledActionModeMenuItems}.
+     */
+    public int getDisabledActionModeMenuItems() {
+        return mBoundaryInterface.getDisabledActionModeMenuItems();
+    }
+
+}
diff --git a/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java b/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java
index f97b2d8..24bb2d1 100644
--- a/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java
+++ b/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java
@@ -42,10 +42,17 @@
         return LAZY_FACTORY_HOLDER.INSTANCE;
     }
 
+    public static WebkitToCompatConverter getCompatConverter() {
+        return LAZY_FACTORY_HOLDER.COMPAT_CONVERTER;
+    }
+
     private static class LAZY_FACTORY_HOLDER {
         static final WebViewProviderFactoryAdapter INSTANCE =
                 new WebViewProviderFactoryAdapter(
                         WebViewGlueCommunicator.createGlueProviderFactory());
+        static final WebkitToCompatConverter COMPAT_CONVERTER =
+                new WebkitToCompatConverter(
+                        INSTANCE.getWebkitToCompatConverter());
     }
 
     private static InvocationHandler fetchGlueProviderFactoryImpl() {
diff --git a/webkit/src/main/java/androidx/webkit/internal/WebViewProviderFactoryAdapter.java b/webkit/src/main/java/androidx/webkit/internal/WebViewProviderFactoryAdapter.java
index d961c09..fe98a56 100644
--- a/webkit/src/main/java/androidx/webkit/internal/WebViewProviderFactoryAdapter.java
+++ b/webkit/src/main/java/androidx/webkit/internal/WebViewProviderFactoryAdapter.java
@@ -21,6 +21,7 @@
 import org.chromium.support_lib_boundary.BoundaryInterfaceReflectionUtil;
 import org.chromium.support_lib_boundary.WebViewProviderBoundaryInterface;
 import org.chromium.support_lib_boundary.WebViewProviderFactoryBoundaryInterface;
+import org.chromium.support_lib_boundary.WebkitToCompatConverterBoundaryInterface;
 
 /**
  * Adapter for WebViewProviderFactoryBoundaryInterface providing static WebView functionality
@@ -42,4 +43,14 @@
         return BoundaryInterfaceReflectionUtil.castToSuppLibClass(
                 WebViewProviderBoundaryInterface.class, mImpl.createWebView(webview));
     }
+
+    /**
+     * Adapter method for creating a new support library version of
+     * {@link androidx.webkit.internal.WebkitToCompatConverter}, which converts android.webkit
+     * classes into their corresponding support library classes.
+     */
+    public WebkitToCompatConverterBoundaryInterface getWebkitToCompatConverter() {
+        return BoundaryInterfaceReflectionUtil.castToSuppLibClass(
+                WebkitToCompatConverterBoundaryInterface.class, mImpl.getWebkitToCompatConverter());
+    }
 }
diff --git a/webkit/src/main/java/androidx/webkit/internal/WebkitToCompatConverter.java b/webkit/src/main/java/androidx/webkit/internal/WebkitToCompatConverter.java
new file mode 100644
index 0000000..cb40530
--- /dev/null
+++ b/webkit/src/main/java/androidx/webkit/internal/WebkitToCompatConverter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.webkit.internal;
+
+import android.webkit.WebSettings;
+
+import org.chromium.support_lib_boundary.BoundaryInterfaceReflectionUtil;
+import org.chromium.support_lib_boundary.WebSettingsBoundaryInterface;
+import org.chromium.support_lib_boundary.WebkitToCompatConverterBoundaryInterface;
+
+/**
+ * A class providing functionality for converting android.webkit classes into support library
+ * classes.
+ */
+public class WebkitToCompatConverter {
+    private final WebkitToCompatConverterBoundaryInterface mImpl;
+
+    public WebkitToCompatConverter(WebkitToCompatConverterBoundaryInterface impl) {
+        mImpl = impl;
+    }
+
+    /**
+     * Return a WebSettingsAdapter linked to webSettings such that calls on either of those
+     * objects affect the other object. That WebSettingsAdapter can be used to implement
+     * {@link androidx.webkit.WebSettingsCompat}.
+     */
+    public WebSettingsAdapter convertSettings(WebSettings webSettings) {
+        return new WebSettingsAdapter(BoundaryInterfaceReflectionUtil.castToSuppLibClass(
+                WebSettingsBoundaryInterface.class, mImpl.convertSettings(webSettings)));
+    }
+}