Merge "Resolve ignored returned Futures in media2-session tests" into androidx-master-dev
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 550b016..30cedff 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -44,6 +44,8 @@
       <option name="IGNORE_POINT_TO_ITSELF" value="false" />
       <option name="myAdditionalJavadocTags" value="hide" />
     </inspection_tool>
+    <inspection_tool class="KDocUnresolvedReference" enabled="true" level="ERROR" enabled_by_default="true" />
+    <inspection_tool class="LongLine" enabled="true" level="WARNING" enabled_by_default="true" />
     <inspection_tool class="MissingDeprecatedAnnotation" enabled="true" level="ERROR" enabled_by_default="true" />
     <inspection_tool class="NullableProblems" enabled="true" level="ERROR" enabled_by_default="true">
       <option name="REPORT_NULLABLE_METHOD_OVERRIDES_NOTNULL" value="true" />
@@ -67,6 +69,7 @@
     </inspection_tool>
     <inspection_tool class="RawTypeCanBeGeneric" enabled="true" level="ERROR" enabled_by_default="true" />
     <inspection_tool class="RawUseOfParameterizedType" enabled="true" level="ERROR" enabled_by_default="true" />
+    <inspection_tool class="Reformat" enabled="true" level="WARNING" enabled_by_default="true" />
     <inspection_tool class="RemoveEmptyParenthesesFromAnnotationEntry" enabled="true" level="WEAK WARNING" enabled_by_default="true">
       <scope name="Compose" level="WEAK WARNING" enabled="false" />
     </inspection_tool>
diff --git a/annotation/annotation/README.md b/annotation/annotation/README.md
index b7f5912..e65290d 100644
--- a/annotation/annotation/README.md
+++ b/annotation/annotation/README.md
@@ -7,7 +7,7 @@
 
 [Release notes](https://developer.android.com/jetpack/androidx/releases/annotation)
 
-[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/annotations/)
+[Browse source](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/annotation/annotation/)
 
 [Reference documentation](https://developer.android.com/reference/androidx/classes.html)
 
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/ParameterizedBenchmark.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/ParameterizedBenchmark.kt
index 050c32d..09a21f4 100644
--- a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/ParameterizedBenchmark.kt
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/ParameterizedBenchmark.kt
@@ -23,14 +23,18 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameters
 
 @LargeTest
 @RunWith(Parameterized::class)
-class ParameterizedBenchmark(@Suppress("unused") private val input: Int) {
+class ParameterizedBenchmark(
+    @Suppress("unused") private val input: Int,
+    @Suppress("unused") private val stringInput: String
+) {
     companion object {
         @JvmStatic
-        @Parameterized.Parameters
-        fun data(): Collection<Array<Int>> = List(2) { arrayOf(it) }
+        @Parameters(name = "size={0},str:{1}")
+        fun data(): Collection<Array<Any>> = List(2) { arrayOf(it, "$it=:") }
     }
 
     @get:Rule
diff --git a/benchmark/common/build.gradle b/benchmark/common/build.gradle
index 488d327..a6d74c9 100644
--- a/benchmark/common/build.gradle
+++ b/benchmark/common/build.gradle
@@ -32,6 +32,7 @@
 
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    androidTestImplementation(KOTLIN_TEST_COMMON)
 }
 
 androidx {
diff --git a/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt b/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
index d48f976..5783636 100644
--- a/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
+++ b/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
@@ -24,6 +24,7 @@
 import org.junit.rules.TemporaryFolder
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
+import kotlin.test.assertTrue
 
 @SmallTest
 @RunWith(JUnit4::class)
@@ -90,6 +91,7 @@
                 "benchmarks": [
                     {
                         "name": "MethodA",
+                        "params": {},
                         "className": "package.Class1",
                         "totalRunTimeNs": 900000000,
                         "metrics": {
@@ -110,6 +112,7 @@
                     },
                     {
                         "name": "MethodB",
+                        "params": {},
                         "className": "package.Class2",
                         "totalRunTimeNs": 900000000,
                         "metrics": {
@@ -134,4 +137,61 @@
             tempFile.readText()
         )
     }
+
+    @Test
+    fun validateJsonWithParams() {
+        val reportWithParams = BenchmarkState.Report(
+            testName = "MethodWithParams[number=2,primeNumber=true]",
+            className = "package.Class",
+            totalRunTimeNs = 900000000,
+            data = listOf(100, 101, 102),
+            repeatIterations = 100000,
+            thermalThrottleSleepSeconds = 90000000,
+            warmupIterations = 8000
+        )
+
+        val tempFile = tempFolder.newFile()
+        ResultWriter.writeReport(tempFile, listOf(reportWithParams))
+        val reportText = tempFile.readText()
+
+        assertTrue {
+            reportText.contains(
+                """
+                |            "name": "MethodWithParams[number=2,primeNumber=true]",
+                |            "params": {
+                |                "number": "2",
+                |                "primeNumber": "true"
+                |            },
+                """.trimMargin()
+            )
+        }
+    }
+
+    @Test
+    fun validateJsonWithInvalidParams() {
+        val reportWithInvalidParams = BenchmarkState.Report(
+            testName = "MethodWithParams[number=2,=true,]",
+            className = "package.Class",
+            totalRunTimeNs = 900000000,
+            data = listOf(100, 101, 102),
+            repeatIterations = 100000,
+            thermalThrottleSleepSeconds = 90000000,
+            warmupIterations = 8000
+        )
+
+        val tempFile = tempFolder.newFile()
+        ResultWriter.writeReport(tempFile, listOf(reportWithInvalidParams))
+        val reportText = tempFile.readText()
+
+        assertTrue {
+            reportText.contains(
+                """
+                |            "name": "MethodWithParams[number=2,=true,]",
+                |            "params": {
+                |                "number": "2"
+                |            },
+                """.trimMargin()
+            )
+        }
+    }
 }
diff --git a/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt b/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt
index 6b6ca2e..593e5dd 100644
--- a/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt
+++ b/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt
@@ -91,16 +91,41 @@
     private fun JsonWriter.reportObject(report: BenchmarkState.Report): JsonWriter {
         beginObject()
             .name("name").value(report.testName)
+            .name("params").paramsObject(report)
             .name("className").value(report.className)
             .name("totalRunTimeNs").value(report.totalRunTimeNs)
             .name("metrics").metricsObject(report)
             .name("warmupIterations").value(report.warmupIterations)
             .name("repeatIterations").value(report.repeatIterations)
             .name("thermalThrottleSleepSeconds").value(report.thermalThrottleSleepSeconds)
-
         return endObject()
     }
 
+    private fun JsonWriter.paramsObject(report: BenchmarkState.Report): JsonWriter {
+        beginObject()
+        getParams(report.testName).forEach { name(it.key).value(it.value) }
+        return endObject()
+    }
+
+    private fun getParams(testName: String): Map<String, String> {
+        val parameterStrStart = testName.indexOf('[')
+        val parameterStrEnd = testName.lastIndexOf(']')
+
+        val params = HashMap<String, String>()
+        if (parameterStrStart >= 0 && parameterStrEnd >= 0) {
+            val paramListString = testName.substring(parameterStrStart + 1, parameterStrEnd)
+            paramListString.split(",").forEach { paramString ->
+                val separatorIndex = paramString.indexOfFirst { it == ':' || it == '=' }
+                if (separatorIndex in 1 until paramString.length - 1) {
+                    val key = paramString.substring(0, separatorIndex)
+                    val value = paramString.substring(separatorIndex + 1)
+                    params[key] = value
+                }
+            }
+        }
+        return params
+    }
+
     private fun JsonWriter.metricsObject(report: BenchmarkState.Report): JsonWriter {
         beginObject()
 
diff --git a/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt b/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt
index 1a57e4f9..85617f0 100644
--- a/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt
+++ b/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt
@@ -17,8 +17,8 @@
 package androidx.benchmark.gradle
 
 import com.android.build.gradle.AppExtension
-import com.android.build.gradle.BaseExtension
 import com.android.build.gradle.LibraryExtension
+import com.android.build.gradle.TestedExtension
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 import org.gradle.api.tasks.StopExecutionException
@@ -57,15 +57,24 @@
     private fun configureWithAndroidPlugin(project: Project) {
         if (!foundAndroidPlugin) {
             foundAndroidPlugin = true
-            val extension = project.extensions.getByType(BaseExtension::class.java)
+            val extension = project.extensions.getByType(TestedExtension::class.java)
             configureWithAndroidExtension(project, extension)
         }
     }
 
-    private fun configureWithAndroidExtension(project: Project, extension: BaseExtension) {
+    private fun configureWithAndroidExtension(project: Project, extension: TestedExtension) {
         val defaultConfig = extension.defaultConfig
         val testInstrumentationArgs = defaultConfig.testInstrumentationRunnerArguments
 
+        defaultConfig.testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
+
+        // disable overhead from test coverage by default, even if we use a debug variant
+        extension.buildTypes.getByName("debug").isTestCoverageEnabled = false
+
+        // Benchmarks default to release build type to avoid pulling in debug libraries, which
+        // may have been compiled with coverage enabled.
+        extension.testBuildType = "release"
+
         // Registering this block as a configureEach callback is only necessary because Studio skips
         // Gradle if there are no changes, which stops this plugin from being re-applied.
         var enabledOutput = false
diff --git a/benchmark/gradle-plugin/src/test/kotlin/androidx/benchmark/gradle/BenchmarkPluginTest.kt b/benchmark/gradle-plugin/src/test/kotlin/androidx/benchmark/gradle/BenchmarkPluginTest.kt
index 73a987d..61974bc 100644
--- a/benchmark/gradle-plugin/src/test/kotlin/androidx/benchmark/gradle/BenchmarkPluginTest.kt
+++ b/benchmark/gradle-plugin/src/test/kotlin/androidx/benchmark/gradle/BenchmarkPluginTest.kt
@@ -326,4 +326,128 @@
         val argsOutput = gradleRunner.withArguments("printInstrumentationArgs").build()
         assertTrue { argsOutput.output.contains("no-isolated-storage:1") }
     }
+
+    @Test
+    fun applyPluginDefaultAgpProperties() {
+        buildFile.writeText(
+            """
+            import com.android.build.gradle.TestedExtension
+
+            plugins {
+                id('com.android.library')
+                id('androidx.benchmark')
+            }
+
+            repositories {
+                maven { url "$prebuiltsRepo/androidx/external" }
+                maven { url "$prebuiltsRepo/androidx/internal" }
+            }
+
+            android {
+                compileSdkVersion $compileSdkVersion
+                buildToolsVersion "$buildToolsVersion"
+
+                defaultConfig {
+                    minSdkVersion $minSdkVersion
+                }
+            }
+
+            dependencies {
+                androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
+
+            }
+
+            tasks.register("printTestInstrumentationRunner") {
+                println android.defaultConfig.testInstrumentationRunner
+            }
+
+            tasks.register("printTestCoverageEnabled") {
+                def extension = project.extensions.getByType(TestedExtension)
+                println extension.buildTypes.getByName("debug").testCoverageEnabled
+            }
+
+            tasks.register("printTestBuildType") {
+                def extension = project.extensions.getByType(TestedExtension)
+                println extension.testBuildType = "release"
+            }
+        """.trimIndent()
+        )
+
+        val runnerOutput = gradleRunner.withArguments("printTestInstrumentationRunner").build()
+        assertTrue {
+            runnerOutput.output.contains("androidx.benchmark.junit4.AndroidBenchmarkRunner")
+        }
+
+        val codeCoverageOutput = gradleRunner.withArguments("printTestCoverageEnabled").build()
+        assertTrue { codeCoverageOutput.output.contains("false") }
+
+        val testBuildTypeOutput = gradleRunner.withArguments("printTestBuildType").build()
+        assertTrue { testBuildTypeOutput.output.contains("release") }
+    }
+
+    @Test
+    fun applyPluginOverrideAgpProperties() {
+        buildFile.writeText(
+            """
+            import com.android.build.gradle.TestedExtension
+
+            plugins {
+                id('com.android.library')
+                id('androidx.benchmark')
+            }
+
+            repositories {
+                maven { url "$prebuiltsRepo/androidx/external" }
+                maven { url "$prebuiltsRepo/androidx/internal" }
+            }
+
+            android {
+                compileSdkVersion $compileSdkVersion
+                buildToolsVersion "$buildToolsVersion"
+                testBuildType = "debug"
+
+                defaultConfig {
+                    minSdkVersion $minSdkVersion
+                    testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+                }
+
+                buildTypes {
+                    debug {
+                        testCoverageEnabled = true
+                    }
+                }
+            }
+
+            dependencies {
+                androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
+
+            }
+
+            tasks.register("printTestInstrumentationRunner") {
+                println android.defaultConfig.testInstrumentationRunner
+            }
+
+            tasks.register("printTestCoverageEnabled") {
+                def extension = project.extensions.getByType(TestedExtension)
+                println extension.buildTypes.getByName("debug").testCoverageEnabled
+            }
+
+            tasks.register("printTestBuildType") {
+                def extension = project.extensions.getByType(TestedExtension)
+                println extension.testBuildType
+            }
+        """.trimIndent()
+        )
+
+        val runnerOutput = gradleRunner.withArguments("printTestInstrumentationRunner").build()
+        assertTrue {
+            runnerOutput.output.contains("androidx.test.runner.AndroidJUnitRunner")
+        }
+
+        val codeCoverageOutput = gradleRunner.withArguments("printTestCoverageEnabled").build()
+        assertTrue { codeCoverageOutput.output.contains("true") }
+
+        val testBuildTypeOutput = gradleRunner.withArguments("printTestBuildType").build()
+        assertTrue { testBuildTypeOutput.output.contains("debug") }
+    }
 }
diff --git a/benchmark/junit4/build.gradle b/benchmark/junit4/build.gradle
index 6fa16a5..d4f47cc 100644
--- a/benchmark/junit4/build.gradle
+++ b/benchmark/junit4/build.gradle
@@ -27,7 +27,7 @@
 
 android {
     defaultConfig {
-        testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"
+        testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
     }
 }
 
diff --git a/buildSrc-tests/build.gradle b/buildSrc-tests/build.gradle
new file mode 100644
index 0000000..f2a49f8
--- /dev/null
+++ b/buildSrc-tests/build.gradle
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This project contains tests for code contained in buildSrc
+// This project is stored outside of buildSrc/ so that waiting for these tests to complete doesn't delay the rest of the build
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.BuildServerConfigurationKt
+
+plugins {
+    id("AndroidXPlugin")
+    id("kotlin")
+}
+
+dependencies {
+    implementation gradleApi()
+    testImplementation JUNIT
+    implementation(project.files(new File(BuildServerConfigurationKt.getRootOutDirectory(project), "buildSrc/build/libs/buildSrc.jar")))
+}
+
+androidx {
+    toolingProject = true
+}
diff --git a/buildSrc-tests/lint-checks/build.gradle b/buildSrc-tests/lint-checks/build.gradle
new file mode 100644
index 0000000..9e9f531
--- /dev/null
+++ b/buildSrc-tests/lint-checks/build.gradle
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import androidx.build.BuildServerConfigurationKt
+
+import java.io.File
+
+plugins {
+    id("AndroidXPlugin")
+    id("kotlin")
+}
+
+dependencies {
+    implementation build_libs.lint.core
+    implementation build_libs.lint.api
+    implementation build_libs.kotlin.stdlib
+    testImplementation build_libs.lint.tests
+    api("androidx.annotation:annotation:1.0.0")
+    implementation project.files(new File(BuildServerConfigurationKt.getRootOutDirectory(project), "buildSrc/lint-checks/build/libs/lint-checks.jar"))
+}
+
+androidx {
+    toolingProject = true
+}
diff --git a/buildSrc/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt b/buildSrc-tests/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
similarity index 99%
rename from buildSrc/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
rename to buildSrc-tests/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
index 30bc8ee..a5f30df 100644
--- a/buildSrc/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
+++ b/buildSrc-tests/lint-checks/src/test/java/androidx/build/lint/ObsoleteBuildCompatUsageDetectorTest.kt
@@ -19,6 +19,7 @@
 import com.android.tools.lint.checks.infrastructure.TestFiles.java
 import com.android.tools.lint.checks.infrastructure.TestLintResult
 import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
+
 import org.junit.Ignore
 import org.junit.Test
 
diff --git a/buildSrc/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt b/buildSrc-tests/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt
similarity index 100%
rename from buildSrc/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt
rename to buildSrc-tests/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt
diff --git a/buildSrc/src/test/kotlin/androidx/build/VersionTest.kt b/buildSrc-tests/src/test/kotlin/androidx/build/VersionTest.kt
similarity index 100%
rename from buildSrc/src/test/kotlin/androidx/build/VersionTest.kt
rename to buildSrc-tests/src/test/kotlin/androidx/build/VersionTest.kt
diff --git a/buildSrc/src/test/kotlin/androidx/build/dependencyTracker/AffectedModuleDetectorImplTest.kt b/buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/AffectedModuleDetectorImplTest.kt
similarity index 100%
rename from buildSrc/src/test/kotlin/androidx/build/dependencyTracker/AffectedModuleDetectorImplTest.kt
rename to buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/AffectedModuleDetectorImplTest.kt
diff --git a/buildSrc/src/test/kotlin/androidx/build/dependencyTracker/AttachLogsTestRule.kt b/buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/AttachLogsTestRule.kt
similarity index 100%
rename from buildSrc/src/test/kotlin/androidx/build/dependencyTracker/AttachLogsTestRule.kt
rename to buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/AttachLogsTestRule.kt
diff --git a/buildSrc/src/test/kotlin/androidx/build/dependencyTracker/BuildPropParserTest.kt b/buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/BuildPropParserTest.kt
similarity index 100%
rename from buildSrc/src/test/kotlin/androidx/build/dependencyTracker/BuildPropParserTest.kt
rename to buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/BuildPropParserTest.kt
diff --git a/buildSrc/src/test/kotlin/androidx/build/dependencyTracker/GitClientImplTest.kt b/buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/GitClientImplTest.kt
similarity index 100%
rename from buildSrc/src/test/kotlin/androidx/build/dependencyTracker/GitClientImplTest.kt
rename to buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/GitClientImplTest.kt
diff --git a/buildSrc/src/test/kotlin/androidx/build/dependencyTracker/ProjectGraphTest.kt b/buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/ProjectGraphTest.kt
similarity index 100%
rename from buildSrc/src/test/kotlin/androidx/build/dependencyTracker/ProjectGraphTest.kt
rename to buildSrc-tests/src/test/kotlin/androidx/build/dependencyTracker/ProjectGraphTest.kt
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index c51fd30..5b08e3b 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -44,13 +44,6 @@
 allprojects {
     repos.addMavenRepositories(repositories)
 
-    tasks.withType(Test) {
-        testLogging {
-            events = ["failed"]
-            exceptionFormat "full"
-        }
-    }
-
     tasks.withType(KotlinCompile).configureEach {
         kotlinOptions {
             freeCompilerArgs += ["-Werror"]
@@ -65,7 +58,6 @@
     implementation build_libs.kotlin.gradle_plugin
     implementation gradleApi()
     implementation project("jetpad-integration")
-    testImplementation "junit:junit:4.12"
 }
 
 apply plugin: "java-gradle-plugin"
diff --git a/buildSrc/lint-checks/build.gradle b/buildSrc/lint-checks/build.gradle
index 6176fc3..1e34235 100644
--- a/buildSrc/lint-checks/build.gradle
+++ b/buildSrc/lint-checks/build.gradle
@@ -20,7 +20,6 @@
     implementation build_libs.lint.core
     implementation build_libs.lint.api
     implementation build_libs.kotlin.stdlib
-    testImplementation build_libs.lint.tests
     api("androidx.annotation:annotation:1.0.0")
 }
 jar {
diff --git a/buildSrc/out.gradle b/buildSrc/out.gradle
index 7ad4cbf..4fe58ca 100644
--- a/buildSrc/out.gradle
+++ b/buildSrc/out.gradle
@@ -24,7 +24,10 @@
     def outDir = System.env.OUT_DIR
     if (outDir == null) {
         outDir = new File("${buildscript.getSourceFile().parent}/../../../out${subdir}")
+    } else {
+        outDir = new File(outDir)
     }
+    project.ext.outDir = outDir
     buildDir = new File(outDir, "$project.name/build")
                 .getCanonicalFile()
     subprojects {
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
index 35fedcb..d77740c 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
@@ -16,7 +16,7 @@
 
 package androidx.build
 
-import androidx.build.SupportConfig.BENCHMARK_INSTRUMENTATION_RUNNER
+import androidx.benchmark.gradle.BenchmarkPlugin
 import androidx.build.SupportConfig.BUILD_TOOLS_VERSION
 import androidx.build.SupportConfig.COMPILE_SDK_VERSION
 import androidx.build.SupportConfig.DEFAULT_MIN_SDK_VERSION
@@ -359,8 +359,13 @@
         compileSdkVersion(COMPILE_SDK_VERSION)
         buildToolsVersion = BUILD_TOOLS_VERSION
         defaultConfig.targetSdkVersion(TARGET_SDK_VERSION)
-        defaultConfig.testInstrumentationRunner =
-            if (project.isBenchmark()) BENCHMARK_INSTRUMENTATION_RUNNER else INSTRUMENTATION_RUNNER
+
+        defaultConfig.testInstrumentationRunner = INSTRUMENTATION_RUNNER
+
+        // Enable code coverage for debug builds only if we are not running inside the IDE, since
+        // enabling coverage reports breaks the method parameter resolution in the IDE debugger.
+        buildTypes.getByName("debug").isTestCoverageEnabled =
+            !project.hasProperty("android.injected.invoked.from.ide")
 
         // Pass the --no-window-animation flag with a hack (b/138120842)
         // NOTE - We're exploiting the fact that anything after a space in the value of a
@@ -416,12 +421,6 @@
 
         project.configureErrorProneForAndroid(variants)
 
-        // Enable code coverage for debug builds only if we are not running inside the IDE, since
-        // enabling coverage reports breaks the method parameter resolution in the IDE debugger.
-        buildTypes.getByName("debug").isTestCoverageEnabled =
-                !project.hasProperty("android.injected.invoked.from.ide") &&
-                !project.isBenchmark()
-
         // Set the officially published version to be the debug version with minimum dependency
         // versions.
         defaultPublishConfig(Release.DEFAULT_PUBLISH_CONFIG)
@@ -476,10 +475,9 @@
                             // Exclude media-compat-test-* and media2-test-* modules from
                             // existing support library presubmit tests.
                             fileName.replace("-debug-androidTest", "")
-                        } else if (fileName.contains("-benchmark-debug-androidTest")) {
-                            // Exclude '-benchmark' modules from correctness tests, and
-                            // remove '-debug' from the APK name, since it's incorrect
-                            fileName.replace("-debug-androidTest", "-androidBenchmark")
+                        } else if (project.plugins.hasPlugin(BenchmarkPlugin::class.java)) {
+                            // Exclude '-benchmark' modules from correctness tests
+                            fileName.replace("-androidTest", "-androidBenchmark")
                         } else {
                             // multiple modules may have the same name so prefix the name with
                             // the module's path to ensure it is unique.
@@ -629,11 +627,6 @@
     }
 }
 
-fun Project.isBenchmark(): Boolean {
-    // benchmark convention is to end name with "-benchmark"
-    return name.endsWith("-benchmark")
-}
-
 fun Project.hideJavadocTask() {
     // Most tasks named "javadoc" are unused
     // So, few tasks named "javadoc" are interesting to developers
diff --git a/buildSrc/src/main/kotlin/androidx/build/BuildServerConfiguration.kt b/buildSrc/src/main/kotlin/androidx/build/BuildServerConfiguration.kt
index 4d065ed..aa1f726 100644
--- a/buildSrc/src/main/kotlin/androidx/build/BuildServerConfiguration.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/BuildServerConfiguration.kt
@@ -46,6 +46,13 @@
 }
 
 /**
+ * Returns the out directory (an ancestor of all files generated by the build)
+ */
+fun Project.getRootOutDirectory(): File {
+    return project.rootProject.extensions.extraProperties.get("outDir") as File
+}
+
+/**
  * Directory to put build info files for release service dependency files.
  */
 fun Project.getBuildInfoDirectory(): File =
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index 6c5bd2c..0b2fb46 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -51,7 +51,7 @@
             .addStubs("car/stubs/android.car.jar")
     prebuilts(LibraryGroups.CARDVIEW, "1.0.0")
     prebuilts(LibraryGroups.COLLECTION, "1.1.0")
-    prebuilts(LibraryGroups.CONCURRENT, "1.0.0-beta01")
+    prebuilts(LibraryGroups.CONCURRENT, "1.0.0-rc01")
     prebuilts(LibraryGroups.CONTENTPAGER, "1.0.0")
     prebuilts(LibraryGroups.COORDINATORLAYOUT, "1.1.0-beta01")
     prebuilts(LibraryGroups.CORE, "core", "1.2.0-alpha02")
@@ -79,11 +79,11 @@
     ignore(LibraryGroups.LOADER.group, "loader-ktx")
     prebuilts(LibraryGroups.LOADER, "1.1.0-rc01")
     prebuilts(LibraryGroups.LOCALBROADCASTMANAGER, "1.1.0-alpha01")
-    prebuilts(LibraryGroups.MEDIA, "media", "1.1.0-rc01")
+    prebuilts(LibraryGroups.MEDIA, "media", "1.1.0")
     ignore(LibraryGroups.MEDIA2.group, "media2-exoplayer")
     prebuilts(LibraryGroups.MEDIA2, "media2-widget", "1.0.0-beta01")
     prebuilts(LibraryGroups.MEDIA2, "1.0.0-rc01")
-    prebuilts(LibraryGroups.MEDIAROUTER, "1.1.0-rc01")
+    prebuilts(LibraryGroups.MEDIAROUTER, "1.1.0")
     ignore(LibraryGroups.NAVIGATION.group, "navigation-testing")
     ignore(LibraryGroups.NAVIGATION.group, "navigation-safe-args-generator")
     ignore(LibraryGroups.NAVIGATION.group, "navigation-safe-args-gradle-plugin")
diff --git a/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt b/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt
index f9b32b8d..4994312 100644
--- a/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt
@@ -23,7 +23,6 @@
 object SupportConfig {
     const val DEFAULT_MIN_SDK_VERSION = 14
     const val INSTRUMENTATION_RUNNER = "androidx.test.runner.AndroidJUnitRunner"
-    const val BENCHMARK_INSTRUMENTATION_RUNNER = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
     const val BUILD_TOOLS_VERSION = "28.0.3"
 
     /**
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
index c20ff94..d3df2ff 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
@@ -48,7 +48,7 @@
  *  ALL_AFFECTED_PROJECTS -- The union of CHANGED_PROJECTS and DEPENDENT_PROJECTS,
  *      which encompasses all projects that could possibly break due to the changes.
  */
-internal enum class ProjectSubset { DEPENDENT_PROJECTS, CHANGED_PROJECTS, ALL_AFFECTED_PROJECTS }
+enum class ProjectSubset { DEPENDENT_PROJECTS, CHANGED_PROJECTS, ALL_AFFECTED_PROJECTS }
 
 /**
  * A utility class that can discover which files are changed based on git history.
@@ -183,7 +183,7 @@
  *
  * When a file in a module is changed, all modules that depend on it are considered as changed.
  */
-internal class AffectedModuleDetectorImpl constructor(
+class AffectedModuleDetectorImpl constructor(
     private val rootProject: Project,
     private val logger: Logger?,
         // used for debugging purposes when we want to ignore non module files
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/BuildPropParser.kt b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/BuildPropParser.kt
index c877e7a..8f48cd6 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/BuildPropParser.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/BuildPropParser.kt
@@ -24,7 +24,7 @@
  *
  * Currently, we don't use it since build system does not give us the right shas.
  */
-internal object BuildPropParser {
+object BuildPropParser {
     /**
      * Returns the sha which is the reference sha that we should use to find changed files.
      *
@@ -90,4 +90,4 @@
         val repoSha: String,
         val buildSha: String
     )
-}
\ No newline at end of file
+}
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/GitClient.kt b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/GitClient.kt
index 7e57766..486df04 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/GitClient.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/GitClient.kt
@@ -20,7 +20,7 @@
 import java.io.File
 import java.util.concurrent.TimeUnit
 
-internal interface GitClient {
+interface GitClient {
     fun findChangedFilesSince(
         sha: String,
         top: String = "HEAD",
@@ -42,7 +42,7 @@
  * A simple git client that uses system process commands to communicate with the git setup in the
  * given working directory.
  */
-internal class GitClientImpl(
+class GitClientImpl(
     /**
      * The root location for git
      */
@@ -109,7 +109,7 @@
     }
 
     companion object {
-        internal const val PREV_MERGE_CMD = "git log -1 --merges --oneline"
-        internal const val CHANGED_FILES_CMD_PREFIX = "git diff --name-only"
+        const val PREV_MERGE_CMD = "git log -1 --merges --oneline"
+        const val CHANGED_FILES_CMD_PREFIX = "git diff --name-only"
     }
 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/dokka/Dokka.kt b/buildSrc/src/main/kotlin/androidx/build/dokka/Dokka.kt
index f43a945..fd330de 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dokka/Dokka.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dokka/Dokka.kt
@@ -25,6 +25,7 @@
 import com.android.build.gradle.LibraryExtension
 import org.gradle.api.Project
 import org.gradle.api.plugins.JavaBasePlugin
+import org.gradle.api.tasks.TaskProvider
 import org.gradle.api.tasks.bundling.Zip
 import org.gradle.kotlin.dsl.apply
 import org.jetbrains.dokka.gradle.DokkaAndroidPlugin
@@ -33,9 +34,11 @@
 import java.io.File
 
 object Dokka {
-    fun generatorTaskNameForType(docsType: String): String {
-        return "dokka${docsType}Docs"
+    fun generatorTaskNameForType(docsType: String, language: String = ""): String {
+        val formattedLangauage = language.toLowerCase().capitalize()
+        return "dokka${formattedLangauage}${docsType}Docs"
     }
+
     fun archiveTaskNameForType(docsType: String): String {
         return "dist${docsType}DokkaDocs"
     }
@@ -44,7 +47,6 @@
         project: Project,
         hiddenPackages: List<String>
     ) {
-        val taskName = generatorTaskNameForType(docsType)
         val archiveTaskName = archiveTaskNameForType(docsType)
         project.apply<DokkaAndroidPlugin>()
         // We don't use the `dokka` task, but it normally appears in `./gradlew tasks`
@@ -53,38 +55,72 @@
         if (project.name != "support" && project.name != "docs-runner") {
             throw Exception("Illegal project passed to createDocsTask: " + project.name)
         }
-        val docsTask = project.tasks.create(taskName, DokkaAndroidTask::class.java) { docsTask ->
-            docsTask.moduleName = project.name
-            docsTask.outputDirectory = File(project.buildDir, taskName).absolutePath
-            docsTask.description = "Generates $docsType Kotlin documentation in the style of " +
-                    "d.android.com.  Places docs in ${docsTask.outputDirectory}"
-            docsTask.outputFormat = "dac"
-            docsTask.outlineRoot = "androidx/"
-            docsTask.dacRoot = "/reference/kotlin"
-            docsTask.moduleName = ""
+
+        val kotlinDocsTask = createDokkaTask(project,
+            docsType,
+            hiddenPackages,
+            "Kotlin",
+            "dac",
+            "/reference/kotlin")
+        val javaDocsTask = createDokkaTask(project,
+            docsType,
+            hiddenPackages,
+            "Java",
+            "dac-as-java",
+            "/reference/")
+
+        project.tasks.register(archiveTaskName, Zip::class.java) { zipTask ->
+
+            zipTask.dependsOn(javaDocsTask)
+            zipTask.from(javaDocsTask.map { it.outputDirectory }) { copySpec ->
+                copySpec.into("reference/java")
+            }
+
+            zipTask.dependsOn(kotlinDocsTask)
+            zipTask.from(kotlinDocsTask.map { it.outputDirectory }) { copySpec ->
+                copySpec.into("reference/kotlin")
+            }
+
+            val buildId = getBuildId()
+            val archiveBaseName = generatorTaskNameForType(docsType)
+            zipTask.archiveBaseName.set(archiveBaseName)
+            zipTask.archiveVersion.set(buildId)
+            zipTask.destinationDirectory.set(project.getDistributionDirectory())
+            val filePath = "${project.getDistributionDirectory().canonicalPath}/"
+            val fileName = "$archiveBaseName-$buildId.zip"
+            zipTask.description = "Zips $docsType documentation (generated via " +
+                "Dokka in the style of d.android.com) into ${filePath + fileName}"
+            zipTask.group = JavaBasePlugin.DOCUMENTATION_GROUP
+        }
+    }
+
+    private fun createDokkaTask(
+        project: Project,
+        docsType: String,
+        hiddenPackages: List<String>,
+        language: String,
+        outputFormat: String,
+        dacRoot: String
+    ): TaskProvider<DokkaAndroidTask> {
+
+        val docTaskName = generatorTaskNameForType(docsType, language)
+
+        return project.tasks.register(docTaskName, DokkaAndroidTask::class.java) { task ->
+            task.moduleName = project.name
+            task.outputDirectory = File(project.buildDir, docTaskName).absolutePath
+            task.description = "Generates $docsType $language documentation in the style of " +
+                    "d.android.com.  Places docs in ${task.outputDirectory}"
+            task.outputFormat = outputFormat
+            task.outlineRoot = "androidx/"
+            task.dacRoot = dacRoot
+            task.moduleName = ""
             for (hiddenPackage in hiddenPackages) {
                 val opts = PackageOptions()
                 opts.prefix = hiddenPackage
                 opts.suppress = true
-                docsTask.perPackageOptions.add(opts)
+                task.perPackageOptions.add(opts)
             }
         }
-
-        project.tasks.create(archiveTaskName, Zip::class.java) { zipTask ->
-            zipTask.dependsOn(docsTask)
-            zipTask.from(docsTask.outputDirectory) { copySpec ->
-                copySpec.into("reference/kotlin")
-            }
-            val buildId = getBuildId()
-            zipTask.archiveBaseName.set(taskName)
-            zipTask.archiveVersion.set(buildId)
-            zipTask.destinationDirectory.set(project.getDistributionDirectory())
-            val filePath = "${project.getDistributionDirectory().canonicalPath}/"
-            val fileName = "$taskName-$buildId.zip"
-            zipTask.description = "Zips $docsType Kotlin documentation (generated via " +
-                "Dokka in the style of d.android.com) into ${filePath + fileName}"
-            zipTask.group = JavaBasePlugin.DOCUMENTATION_GROUP
-        }
     }
 
     fun Project.configureAndroidProjectForDokka(
diff --git a/buildSrc/src/main/kotlin/androidx/build/dokka/DokkaPublicDocs.kt b/buildSrc/src/main/kotlin/androidx/build/dokka/DokkaPublicDocs.kt
index c313a71..c793a8c 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dokka/DokkaPublicDocs.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dokka/DokkaPublicDocs.kt
@@ -20,7 +20,6 @@
 
 import java.io.File
 import androidx.build.androidJarFile
-import androidx.build.java.JavaCompileInputs
 import androidx.build.AndroidXExtension
 import androidx.build.RELEASE_RULE
 import androidx.build.Strategy.Ignore
@@ -30,13 +29,15 @@
 import org.gradle.api.Project
 import org.gradle.api.tasks.TaskAction
 import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.TaskCollection
 import org.gradle.api.tasks.TaskContainer
+import org.gradle.api.tasks.TaskProvider
 import org.gradle.api.tasks.util.PatternFilterable
 import org.jetbrains.dokka.gradle.DokkaTask
 
 object DokkaPublicDocs {
-    val ARCHIVE_TASK_NAME: String = Dokka.archiveTaskNameForType("Public")
-    private val RUNNER_TASK_NAME = Dokka.generatorTaskNameForType("Public")
+    private const val DOCS_TYPE = "Public"
+    val ARCHIVE_TASK_NAME: String = Dokka.archiveTaskNameForType(DOCS_TYPE)
     private const val UNZIP_DEPS_TASK_NAME = "unzipDokkaPublicDocsDeps"
 
     val hiddenPackages = listOf(
@@ -56,6 +57,9 @@
         "androidx.work.impl.utils.futures",
         "androidx.work.impl.utils.taskexecutor")
 
+    private var docsTasks: TaskCollection<DokkaTask>? = null
+    private var unzipTask: LocateJarsTask? = null
+
     fun tryGetRunnerProject(project: Project): Project? {
         return project.rootProject.findProject(":docs-runner")
     }
@@ -64,34 +68,56 @@
         return tryGetRunnerProject(project)!!
     }
 
-    fun getDocsTask(project: Project): DokkaTask {
+    fun getDocsTasks(project: Project): TaskCollection<DokkaTask>? {
+        docsTasks?.let {
+            return it
+        }
         val runnerProject = getRunnerProject(project)
-        return runnerProject.tasks.getOrCreateDocsTask(runnerProject)
+        docsTasks = runnerProject.tasks.getOrCreateDocsTask(runnerProject)
+        return docsTasks
     }
 
     fun getUnzipDepsTask(project: Project): LocateJarsTask {
+        unzipTask?.let {
+            return it
+        }
         val runnerProject = getRunnerProject(project)
-        return runnerProject.tasks.getByName(DokkaPublicDocs.UNZIP_DEPS_TASK_NAME) as LocateJarsTask
+        unzipTask = runnerProject.tasks.getByName(UNZIP_DEPS_TASK_NAME) as LocateJarsTask
+        return unzipTask as LocateJarsTask
     }
 
-    @Synchronized fun TaskContainer.getOrCreateDocsTask(runnerProject: Project): DokkaTask {
+    @Synchronized fun TaskContainer.getOrCreateDocsTask(runnerProject: Project):
+            TaskCollection<DokkaTask> {
         val tasks = this
-        if (tasks.findByName(RUNNER_TASK_NAME) == null) {
-            Dokka.createDocsTask("Public",
+        var dokkaTasks = runnerProject.tasks.withType(DokkaTask::class.java)
+            .matching { it.name.contains(DOCS_TYPE) }
+        if (dokkaTasks.isEmpty()) {
+            Dokka.createDocsTask(
+                DOCS_TYPE,
                 runnerProject,
                 hiddenPackages)
-            val docsTask = runnerProject.tasks.getByName(RUNNER_TASK_NAME) as DokkaTask
+
+            dokkaTasks = runnerProject.tasks.withType(DokkaTask::class.java)
+                .matching { it.name.contains(DOCS_TYPE) }
+
             tasks.create(UNZIP_DEPS_TASK_NAME, LocateJarsTask::class.java) { unzipTask ->
                 unzipTask.doLast {
                     for (jar in unzipTask.outputJars) {
-                        docsTask.classpath = docsTask.classpath.plus(runnerProject.file(jar))
+                        dokkaTasks.forEach {
+                            it.classpath += runnerProject.file(jar)
+                        }
                     }
-                    docsTask.classpath += androidJarFile(runnerProject)
+                    dokkaTasks.forEach {
+                        it.classpath += androidJarFile(runnerProject)
+                    }
                 }
-                docsTask.dependsOn(unzipTask)
+
+                dokkaTasks.forEach {
+                    it.dependsOn(unzipTask)
+                }
             }
         }
-        return runnerProject.tasks.getByName(DokkaPublicDocs.RUNNER_TASK_NAME) as DokkaTask
+        return dokkaTasks
     }
 
     // specifies that <project> exists and might need us to generate documentation for it
@@ -119,14 +145,21 @@
     }
 
     // specifies that <dependency> describes an artifact containing sources that we want to include in our generated documentation
-    private fun registerPrebuilt(dependency: String, runnerProject: Project): Copy {
-        val docsTask = getDocsTask(runnerProject)
+    private fun registerPrebuilt(dependency: String, runnerProject: Project): TaskProvider<Copy> {
+        val dokkaTasks = getDocsTasks(runnerProject)
 
         // unzip the sources jar
         val unzipTask = getPrebuiltSources(runnerProject, "$dependency:sources")
-        val sourceDir = unzipTask.destinationDir
-        docsTask.dependsOn(unzipTask)
-        docsTask.sourceDirs += sourceDir
+        val sourceDir = unzipTask.map { it.destinationDir }
+
+        // Avoid depending on or modifying a task that has already been executed.
+        // Because registerProject is called in an afterEvaluate, we can end up in a case where the dokka tasks are
+        // cached and have been executed in a prior run
+        dokkaTasks?.filter { it.state.isConfigurable }?.forEach {
+            val sourceDirVal = sourceDir.get()
+            it.dependsOn(unzipTask)
+            it.sourceDirs += sourceDirVal
+        }
 
         // also make a note to unzip any dependencies too
         getUnzipDepsTask(runnerProject).inputDependencies.add(dependency)
@@ -138,7 +171,7 @@
     private fun getPrebuiltSources(
         runnerProject: Project,
         mavenId: String
-    ): Copy {
+    ): TaskProvider<Copy> {
         val configuration = runnerProject.configurations.detachedConfiguration(
             runnerProject.dependencies.create(mavenId)
         )
@@ -156,7 +189,7 @@
         val sanitizedMavenId = mavenId.replace(":", "-")
         val buildDir = runnerProject.buildDir
         val destDir = runnerProject.file("$buildDir/sources-unzipped/$sanitizedMavenId")
-        return runnerProject.tasks.create("unzip$sanitizedMavenId", Copy::class.java) {
+        return runnerProject.tasks.register("unzip$sanitizedMavenId", Copy::class.java) {
             it.from(runnerProject.zipTree(configuration.singleFile)
                 .matching {
                     it.exclude("**/*.MF")
@@ -172,14 +205,6 @@
             }
         }
     }
-
-    private fun registerInputs(inputs: JavaCompileInputs, project: Project) {
-        val docsTask = getDocsTask(project)
-        docsTask.sourceDirs += inputs.sourcePaths
-        docsTask.classpath = docsTask.classpath.plus(inputs.dependencyClasspath)
-            .plus(inputs.bootClasspath)
-        docsTask.dependsOn(inputs.dependencyClasspath)
-    }
 }
 
 open class LocateJarsTask : DefaultTask() {
diff --git a/buildSrc/src/main/kotlin/androidx/build/dokka/DokkaSourceDocs.kt b/buildSrc/src/main/kotlin/androidx/build/dokka/DokkaSourceDocs.kt
index 09d95e6..80826b3 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dokka/DokkaSourceDocs.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dokka/DokkaSourceDocs.kt
@@ -18,23 +18,25 @@
 // TODO: after DiffAndDocs and Doclava are fully obsoleted and removed, rename this from DokkaSourceDocs to just SourceDocs
 package androidx.build.dokka
 
-import androidx.build.java.JavaCompileInputs
 import androidx.build.AndroidXExtension
 import androidx.build.defaultPublishVariant
+import androidx.build.java.JavaCompileInputs
 import com.android.build.gradle.LibraryExtension
 import org.gradle.api.Project
 import org.gradle.api.plugins.JavaPluginConvention
+import org.gradle.api.tasks.TaskCollection
 import org.gradle.api.tasks.TaskContainer
 import org.gradle.kotlin.dsl.getPlugin
 import org.jetbrains.dokka.gradle.DokkaTask
 
 object DokkaSourceDocs {
-    private val RUNNER_TASK_NAME = Dokka.generatorTaskNameForType("TipOfTree")
-    public val ARCHIVE_TASK_NAME: String = Dokka.archiveTaskNameForType("TipOfTree")
+    private const val DOCS_TYPE = "TipOfTree"
+    public val ARCHIVE_TASK_NAME: String = Dokka.archiveTaskNameForType(DOCS_TYPE)
     // TODO(b/72330103) make "generateDocs" be the only archive task once Doclava is fully removed
     private val ALTERNATE_ARCHIVE_TASK_NAME: String = "generateDocs"
 
     private val hiddenPackages = DokkaPublicDocs.hiddenPackages
+    private var docsTasks: TaskCollection<DokkaTask>? = null
 
     fun tryGetRunnerProject(project: Project): Project? {
         return project.rootProject.findProject(":docs-runner")
@@ -44,22 +46,33 @@
         return tryGetRunnerProject(project)!!
     }
 
-    fun getDocsTask(project: Project): DokkaTask {
-        var runnerProject = getRunnerProject(project)
-        return runnerProject.tasks.getOrCreateDocsTask(runnerProject)
+    fun getDocsTasks(project: Project): TaskCollection<DokkaTask>? {
+        docsTasks?.let {
+            return it
+        }
+        val runnerProject = getRunnerProject(project)
+        docsTasks = runnerProject.tasks.getOrCreateDocsTask(runnerProject)
+        return docsTasks
     }
 
-    @Synchronized fun TaskContainer.getOrCreateDocsTask(runnerProject: Project): DokkaTask {
+    @Synchronized fun TaskContainer.getOrCreateDocsTask(runnerProject: Project):
+            TaskCollection<DokkaTask> {
         val tasks = this
-        if (tasks.findByName(DokkaSourceDocs.RUNNER_TASK_NAME) == null) {
-            Dokka.createDocsTask("TipOfTree", runnerProject, hiddenPackages)
-            if (tasks.findByName(DokkaSourceDocs.ALTERNATE_ARCHIVE_TASK_NAME) == null) {
-                tasks.create(ALTERNATE_ARCHIVE_TASK_NAME)
+        var dokkaTasks = runnerProject.tasks.withType(DokkaTask::class.java)
+            .matching { it.name.contains(DOCS_TYPE) }
+
+        if (dokkaTasks.isEmpty()) {
+            Dokka.createDocsTask(DOCS_TYPE, runnerProject, hiddenPackages)
+            dokkaTasks = runnerProject.tasks.withType(DokkaTask::class.java)
+                .matching { it.name.contains(DOCS_TYPE) }
+
+            if (tasks.findByName(ALTERNATE_ARCHIVE_TASK_NAME) == null) {
+                tasks.register(ALTERNATE_ARCHIVE_TASK_NAME) {
+                    it.dependsOn(tasks.named(ARCHIVE_TASK_NAME))
+                }
             }
-            tasks.getByName(ALTERNATE_ARCHIVE_TASK_NAME)
-                .dependsOn(tasks.getByName(ARCHIVE_TASK_NAME))
         }
-        return tasks.getByName(DokkaSourceDocs.RUNNER_TASK_NAME) as DokkaTask
+        return dokkaTasks
     }
 
     fun registerAndroidProject(
@@ -95,17 +108,24 @@
         }
         val javaPluginConvention = project.convention.getPlugin<JavaPluginConvention>()
         val mainSourceSet = javaPluginConvention.sourceSets.getByName("main")
-        project.afterEvaluate({
+        project.afterEvaluate {
             val inputs = JavaCompileInputs.fromSourceSet(mainSourceSet, project)
             registerInputs(inputs, project)
-        })
+        }
     }
 
     fun registerInputs(inputs: JavaCompileInputs, project: Project) {
-        val docsTask = getDocsTask(project)
-        docsTask.sourceDirs += inputs.sourcePaths
-        docsTask.classpath = docsTask.classpath.plus(inputs.dependencyClasspath)
-            .plus(inputs.bootClasspath)
-        docsTask.dependsOn(inputs.dependencyClasspath)
+        val dokkaTasks = getDocsTasks(project)
+
+        // Avoid depending on or modifying a task that has already been executed.
+        // Because registerInputs is called in an afterEvaluate, we can end up in a case where the dokka tasks are
+        // cached and have been executed in a prior run
+        dokkaTasks?.filter { it.state.isConfigurable }?.forEach {
+            it.sourceDirs += inputs.sourcePaths
+
+            it.classpath = it.classpath.plus(inputs.dependencyClasspath)
+                .plus(inputs.bootClasspath)
+            it.dependsOn(inputs.dependencyClasspath)
+        }
     }
 }
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
index b552085..67292a2 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
@@ -84,8 +84,9 @@
     @Rule
     public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(
             Manifest.permission.CAMERA);
+
     @Before
-    public void setUp()  {
+    public void setUp() {
         assumeTrue(CameraUtil.deviceHasCamera());
         mAnalysisResults = new HashSet<>();
         mAnalysisResultsSemaphore = new Semaphore(/*permits=*/ 0);
@@ -168,11 +169,23 @@
     }
 
     @Test
-    public void analyzerAnalyzesImages_whenCameraIsOpen()
+    public void analyzesImages_withAcquireLatest_whenCameraIsOpen()
+            throws InterruptedException, CameraInfoUnavailableException {
+        analyzerAnalyzesImagesWithMode(ImageReaderMode.ACQUIRE_LATEST_IMAGE);
+    }
+
+    @Test
+    public void analyzesImages_withAcquireNext_whenCameraIsOpen()
+            throws InterruptedException, CameraInfoUnavailableException {
+        analyzerAnalyzesImagesWithMode(ImageReaderMode.ACQUIRE_NEXT_IMAGE);
+    }
+
+    private void analyzerAnalyzesImagesWithMode(ImageReaderMode imageReaderMode)
             throws InterruptedException, CameraInfoUnavailableException {
         final int imageFormat = ImageFormat.YUV_420_888;
         ImageAnalysisConfig config =
-                new ImageAnalysisConfig.Builder().setCallbackHandler(mHandler).build();
+                new ImageAnalysisConfig.Builder().setImageReaderMode(
+                        imageReaderMode).setCallbackHandler(mHandler).build();
         ImageAnalysis useCase = new ImageAnalysis(config);
         Map<String, Size> suggestedResolutionMap = new HashMap<>();
         suggestedResolutionMap.put(mCameraId, DEFAULT_RESOLUTION);
@@ -180,6 +193,8 @@
         CameraUtil.openCameraWithUseCase(mCameraId, mCamera, useCase);
         useCase.setAnalyzer(mAnalyzer);
 
+        mAnalysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS);
+
         int sensorRotation = CameraX.getCameraInfo(mCameraId).getSensorRotationDegrees();
         // The frames should have properties which match the configuration.
         for (ImageProperties properties : mAnalysisResults) {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageReaderProxysTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageReaderProxysTest.java
index f11a503..3f31ab0 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageReaderProxysTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageReaderProxysTest.java
@@ -40,7 +40,7 @@
 import androidx.camera.testing.fakes.FakeUseCaseConfig;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.MediumTest;
+import androidx.test.filters.LargeTest;
 
 import org.junit.After;
 import org.junit.Before;
@@ -53,10 +53,14 @@
 import java.util.Map;
 import java.util.concurrent.Semaphore;
 
-@MediumTest
+/**
+ * Instrument test for {@link ImageReaderProxy}.
+ */
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public final class ImageReaderProxysTest {
     private static final String CAMERA_ID = "0";
+    private static final int TEST_TIMEOUT_MILLIS = 3000;
 
     private BaseCamera mCamera;
     private HandlerThread mHandlerThread;
@@ -102,7 +106,6 @@
         }
     }
 
-    @MediumTest
     @Test
     public void sharedReadersGetFramesFromCamera() throws InterruptedException {
         List<Semaphore> semaphores = new ArrayList<>();
@@ -128,7 +131,6 @@
         }
     }
 
-    @MediumTest
     @Test
     public void isolatedReadersGetFramesFromCamera() throws InterruptedException {
         List<Semaphore> semaphores = new ArrayList<>();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java
index 0679266..fa8fec7 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraX.java
@@ -290,6 +290,8 @@
      * @param lensFacing the lens facing
      * @return true if the device has at least one camera with the specified lens facing,
      * otherwise false.
+     * @throws CameraInfoUnavailableException if unable to access cameras, perhaps due to
+     *                                        insufficient permissions
      */
     public static boolean hasCameraWithLensFacing(LensFacing lensFacing)
             throws CameraInfoUnavailableException {
@@ -325,10 +327,11 @@
      * @return the cameraId if camera exists or {@code null} if no camera found with the config
      * @throws CameraInfoUnavailableException if unable to access cameras, perhaps due to
      *                                        insufficient permissions.
+     * @throws IllegalArgumentException       if there's no lens facing set in the config.
      * @hide
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
-    @NonNull
+    @Nullable
     public static String getCameraWithCameraDeviceConfig(CameraDeviceConfig config)
             throws CameraInfoUnavailableException {
         Set<String> availableCameraIds = getCameraFactory().getAvailableCameraIds();
@@ -338,7 +341,10 @@
             availableCameraIds =
                     LensFacingCameraIdFilter.createLensFacingCameraIdFilter(lensFacing)
                             .filter(availableCameraIds);
+        } else {
+            throw new IllegalArgumentException("Lens facing isn't set in the config.");
         }
+
         CameraIdFilter cameraIdFilter = config.getCameraIdFilter(null);
         if (cameraIdFilter != null) {
             // Filters camera ids with other filters.
@@ -348,7 +354,7 @@
         if (!availableCameraIds.isEmpty()) {
             return availableCameraIds.iterator().next();
         } else {
-            throw new CameraInfoUnavailableException("Unable to find available camera id.");
+            return null;
         }
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ForwardingImageReaderListener.java b/camera/camera-core/src/main/java/androidx/camera/core/ForwardingImageReaderListener.java
index e94c9d60..566db37 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ForwardingImageReaderListener.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ForwardingImageReaderListener.java
@@ -44,6 +44,9 @@
     @Override
     public synchronized void onImageAvailable(ImageReaderProxy imageReaderProxy) {
         ImageProxy imageProxy = imageReaderProxy.acquireNextImage();
+        if (imageProxy == null) {
+            return;
+        }
         ReferenceCountedImageProxy referenceCountedImageProxy =
                 new ReferenceCountedImageProxy(imageProxy);
         for (QueuedImageReaderProxy queuedImageReaderProxy : mImageReaders) {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
index 5733c02..597356a 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
@@ -56,6 +56,9 @@
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final Defaults DEFAULT_CONFIG = new Defaults();
     private static final String TAG = "ImageAnalysis";
+    // ImageReader depth for non-blocking mode.
+    private static final int NON_BLOCKING_IMAGE_DEPTH = 4;
+
     final AtomicReference<Analyzer> mSubscribedAnalyzer;
     final AtomicInteger mRelativeRotation = new AtomicInteger();
     final Handler mHandler;
@@ -260,44 +263,27 @@
         Executor backgroundExecutor = config.getBackgroundExecutor(
                 CameraXExecutors.highPriorityExecutor());
 
+        int imageQueueDepth = config.getImageReaderMode() == ImageReaderMode.ACQUIRE_NEXT_IMAGE
+                ? config.getImageQueueDepth() : NON_BLOCKING_IMAGE_DEPTH;
+
         mImageReader =
                 ImageReaderProxys.createCompatibleReader(
                         cameraId,
                         resolution.getWidth(),
                         resolution.getHeight(),
                         getImageFormat(),
-                        config.getImageQueueDepth(),
+                        imageQueueDepth,
                         backgroundExecutor);
 
         tryUpdateRelativeRotation(cameraId);
-        mImageReader.setOnImageAvailableListener(
-                new ImageReaderProxy.OnImageAvailableListener() {
-                    @Override
-                    public void onImageAvailable(ImageReaderProxy imageReader) {
-                        Analyzer analyzer = mSubscribedAnalyzer.get();
-                        mHandler.post(new Runnable() {
-                            @Override
-                            public void run() {
-                                try (ImageProxy image =
-                                             config
-                                                     .getImageReaderMode(
-                                                             config.getImageReaderMode())
-                                                     .equals(ImageReaderMode.ACQUIRE_NEXT_IMAGE)
-                                                     ? imageReader.acquireNextImage()
-                                                     : imageReader.acquireLatestImage()) {
-                                    // Do not analyze if unable to acquire an ImageProxy
-                                    if (image == null) {
-                                        return;
-                                    }
 
-                                    if (analyzer != null) {
-                                        analyzer.analyze(image, mRelativeRotation.get());
-                                    }
-                                }
-                            }
-                        });
-                    }
-                },
+        ImageReaderProxy.OnImageAvailableListener onImageAvailableListener =
+                config.getImageReaderMode() == ImageReaderMode.ACQUIRE_NEXT_IMAGE
+                        ? new ImageAnalysisBlockingCallback(mSubscribedAnalyzer, mRelativeRotation,
+                        mHandler) :
+                        new ImageAnalysisNonBlockingCallback(mSubscribedAnalyzer, mRelativeRotation,
+                                mHandler, backgroundExecutor);
+        mImageReader.setOnImageAvailableListener(onImageAvailableListener,
                 backgroundExecutor);
 
         SessionConfig.Builder sessionConfigBuilder = SessionConfig.Builder.createFrom(config);
@@ -383,7 +369,7 @@
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final class Defaults implements ConfigProvider<ImageAnalysisConfig> {
         private static final ImageReaderMode DEFAULT_IMAGE_READER_MODE =
-                ImageReaderMode.ACQUIRE_NEXT_IMAGE;
+                ImageReaderMode.ACQUIRE_LATEST_IMAGE;
         private static final Handler DEFAULT_HANDLER = new Handler(Looper.getMainLooper());
         private static final int DEFAULT_IMAGE_QUEUE_DEPTH = 6;
         private static final Size DEFAULT_TARGET_RESOLUTION = new Size(640, 480);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingCallback.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingCallback.java
new file mode 100644
index 0000000..ce59f70
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingCallback.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core;
+
+import android.os.Handler;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * OnImageAvailableListener with blocking behavior. It never drops image without analyzing it.
+ *
+ * <p> Used with {@link ImageAnalysis}.
+ */
+final class ImageAnalysisBlockingCallback implements ImageReaderProxy.OnImageAvailableListener {
+
+    private static final String TAG = "BlockingCallback";
+
+    final AtomicReference<ImageAnalysis.Analyzer> mSubscribedAnalyzer;
+    final AtomicInteger mRelativeRotation;
+
+    private final Handler mUserHandler;
+
+    ImageAnalysisBlockingCallback(AtomicReference<ImageAnalysis.Analyzer> subscribedAnalyzer,
+            AtomicInteger relativeRotation, Handler userHandler) {
+        mSubscribedAnalyzer = subscribedAnalyzer;
+        mRelativeRotation = relativeRotation;
+        mUserHandler = userHandler;
+    }
+
+    @Override
+    public void onImageAvailable(ImageReaderProxy imageReaderProxy) {
+        ImageProxy image = imageReaderProxy.acquireNextImage();
+        if (image == null) {
+            return;
+        }
+        try {
+            mUserHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        ImageAnalysis.Analyzer analyzer = mSubscribedAnalyzer.get();
+                        if (analyzer != null) {
+                            analyzer.analyze(image, mRelativeRotation.get());
+                        }
+                    } finally {
+                        image.close();
+                    }
+                }
+            });
+        } catch (RuntimeException e) {
+            image.close();
+        }
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java
index d7c529b..b834cc7 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java
@@ -573,7 +573,8 @@
         }
 
         /**
-         * Sets the number of images available to the camera pipeline.
+         * Sets the number of images available to the camera pipeline for
+         * {@link ImageReaderMode#ACQUIRE_NEXT_IMAGE} mode.
          *
          * <p>The image queue depth is the number of images available to the camera to fill with
          * data. This includes the image currently being analyzed by {@link
@@ -591,6 +592,10 @@
          * single frame period for the current frame rate, on average, to avoid stalling the camera
          * pipeline.
          *
+         * <p> The value only applys to {@link ImageReaderMode#ACQUIRE_NEXT_IMAGE} mode.
+         * For {@link ImageReaderMode#ACQUIRE_LATEST_IMAGE} the value is overridden by default
+         * value.
+         *
          * @param depth The total number of images available to the camera.
          * @return The current Builder.
          */
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingCallback.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingCallback.java
new file mode 100644
index 0000000..1d226f4
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingCallback.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core;
+
+import android.os.Handler;
+import android.util.Log;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * OnImageAvailableListener with non-blocking behavior. Analyzes images in a non-blocking way by
+ * dropping images when analyzer is busy.
+ *
+ * <p> Used with {@link ImageAnalysis}.
+ */
+final class ImageAnalysisNonBlockingCallback implements ImageReaderProxy.OnImageAvailableListener {
+
+    private static final String TAG = "NonBlockingCallback";
+
+    final AtomicReference<ImageAnalysis.Analyzer> mSubscribedAnalyzer;
+    final AtomicInteger mRelativeRotation;
+
+    final Executor mBackgroundExecutor;
+    private final Handler mUserHandler;
+
+    // The cached image when analyzer is busy. Image removed from cache must be closed by 1) closing
+    // it directly or 2) re-posting it to close it eventually.
+    @GuardedBy("this")
+    private ImageProxy mCachedImage;
+
+    // Timestamp of the last image posted to user callback thread.
+    private final AtomicLong mPostedImageTimestamp;
+    // Timestamp of the last image finished being processed by user callback thread.
+    private final AtomicLong mFinishedImageTimestamp;
+
+    ImageAnalysisNonBlockingCallback(AtomicReference<ImageAnalysis.Analyzer> subscribedAnalyzer,
+            AtomicInteger relativeRotation, Handler userHandler,
+            Executor backgroundExecutor) {
+        mSubscribedAnalyzer = subscribedAnalyzer;
+        mRelativeRotation = relativeRotation;
+        mUserHandler = userHandler;
+        mBackgroundExecutor = backgroundExecutor;
+        mPostedImageTimestamp = new AtomicLong(-1);
+        mFinishedImageTimestamp = new AtomicLong(mPostedImageTimestamp.get());
+    }
+
+    @Override
+    public void onImageAvailable(ImageReaderProxy imageReaderProxy) {
+        ImageProxy imageProxy = imageReaderProxy.acquireLatestImage();
+        if (imageProxy == null) {
+            return;
+        }
+        analyze(imageProxy);
+    }
+
+    /**
+     * Removes cached image from cache and analyze it.
+     */
+    synchronized void analyzeCachedImage() {
+        if (mCachedImage != null) {
+            ImageProxy cachedImage = mCachedImage;
+            mCachedImage = null;
+            analyze(cachedImage);
+        }
+    }
+
+    /**
+     * This method guarantees closing the image by either 1) closing the image in the current
+     * thread, 2) caching it for later or 3) posting it to user Thread to close it.
+     *
+     * @param imageProxy the incoming image frame.
+     */
+    private synchronized void analyze(@NonNull ImageProxy imageProxy) {
+        long postedImageTimestamp = mPostedImageTimestamp.get();
+        long finishedImageTimestamp = mFinishedImageTimestamp.get();
+
+        if (imageProxy.getTimestamp() <= postedImageTimestamp) {
+            // Discard image that is in wrong order. Reposted cached image can be in this state.
+            imageProxy.close();
+            return;
+        }
+
+        if (postedImageTimestamp > finishedImageTimestamp) {
+            // If analyzer is busy, cache the new image.
+            if (mCachedImage != null) {
+                mCachedImage.close();
+            }
+            mCachedImage = imageProxy;
+            return;
+        }
+
+        mPostedImageTimestamp.set(imageProxy.getTimestamp());
+        try {
+            mUserHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        ImageAnalysis.Analyzer analyzer = mSubscribedAnalyzer.get();
+                        if (analyzer != null) {
+                            analyzer.analyze(imageProxy, mRelativeRotation.get());
+                        }
+                    } finally {
+                        finishImage(imageProxy);
+                        mBackgroundExecutor.execute(new Runnable() {
+                            @Override
+                            public void run() {
+                                analyzeCachedImage();
+                            }
+                        });
+                    }
+                }
+            });
+        } catch (RuntimeException e) {
+            // Unblock if fails to post to user thread.
+            Log.e(TAG, "Error calling user callback", e);
+            finishImage(imageProxy);
+        }
+    }
+
+    synchronized void finishImage(ImageProxy imageProxy) {
+        mFinishedImageTimestamp.set(imageProxy.getTimestamp());
+        imageProxy.close();
+    }
+}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
new file mode 100644
index 0000000..2e22966
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.media.Image;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Size;
+
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.internal.DoNotInstrument;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowLooper;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Unit test for {@link ImageAnalysis}.
+ */
+@MediumTest
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP, shadows = {ShadowCameraX.class,
+        ShadowImageReader.class})
+public class ImageAnalysisTest {
+
+    private static final Size DEFAULT_RESOLUTION = new Size(640, 480);
+    private static final int QUEUE_DEPTH = 8;
+    private static final Image MOCK_IMAGE_1 = createMockImage(1);
+    private static final Image MOCK_IMAGE_2 = createMockImage(2);
+    private static final Image MOCK_IMAGE_3 = createMockImage(3);
+
+    private Handler mCallbackHandler;
+    private Handler mBackgroundHandler;
+    private Executor mBackgroundExecutor;
+    private List<Image> mImagesReceived;
+
+    @Before
+    public void setUp() {
+        HandlerThread callbackThread = new HandlerThread("Callback");
+        callbackThread.start();
+        mCallbackHandler = new Handler(callbackThread.getLooper());
+
+        HandlerThread backgroundThread = new HandlerThread("Background");
+        backgroundThread.start();
+        mBackgroundHandler = new Handler(backgroundThread.getLooper());
+        mBackgroundExecutor = CameraXExecutors.newHandlerExecutor(mBackgroundHandler);
+
+        mImagesReceived = new ArrayList<>();
+
+        ShadowImageReader.clear();
+    }
+
+    @Test
+    public void acquireLatestMode_doesNotBlock() {
+        // Arrange.
+        setUpImageAnalysisWithMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE);
+
+        // Act.
+        // Receive images from camera feed.
+        ShadowImageReader.triggerCallbackWithImage(MOCK_IMAGE_1);
+        flushHandler(mBackgroundHandler);
+        ShadowImageReader.triggerCallbackWithImage(MOCK_IMAGE_2);
+        flushHandler(mBackgroundHandler);
+        ShadowImageReader.triggerCallbackWithImage(MOCK_IMAGE_3);
+        flushHandler(mBackgroundHandler);
+
+        // Assert.
+        // No image is received because callback handler is blocked.
+        assertThat(mImagesReceived).isEmpty();
+
+        // Flush callback handler and image1 is received.
+        flushHandler(mCallbackHandler);
+        assertThat(mImagesReceived).containsExactly(MOCK_IMAGE_1);
+
+        // Flush both handlers and the previous cached image3 is received (image2 was dropped). The
+        // code alternates the 2 threads so they have to be both flushed to proceed.
+        flushHandler(mBackgroundHandler);
+        flushHandler(mCallbackHandler);
+        assertThat(mImagesReceived).containsExactly(MOCK_IMAGE_1, MOCK_IMAGE_3);
+
+        // Flush both handlers and no more frame.
+        flushHandler(mBackgroundHandler);
+        flushHandler(mCallbackHandler);
+        assertThat(mImagesReceived).containsExactly(MOCK_IMAGE_1, MOCK_IMAGE_3);
+    }
+
+    @Test
+    public void acquireNextMode_doesNotDropFrames() {
+        // Arrange.
+        setUpImageAnalysisWithMode(ImageAnalysis.ImageReaderMode.ACQUIRE_NEXT_IMAGE);
+
+        // Act.
+        // Receive images from camera feed.
+        ShadowImageReader.triggerCallbackWithImage(MOCK_IMAGE_1);
+        flushHandler(mBackgroundHandler);
+        ShadowImageReader.triggerCallbackWithImage(MOCK_IMAGE_2);
+        flushHandler(mBackgroundHandler);
+        ShadowImageReader.triggerCallbackWithImage(MOCK_IMAGE_3);
+        flushHandler(mBackgroundHandler);
+
+        // Assert.
+        // No image is received because callback handler is blocked.
+        assertThat(mImagesReceived).isEmpty();
+
+        // Flush callback handler and 3 frames received.
+        flushHandler(mCallbackHandler);
+        assertThat(mImagesReceived).containsExactly(MOCK_IMAGE_1, MOCK_IMAGE_2, MOCK_IMAGE_3);
+    }
+
+    private void setUpImageAnalysisWithMode(ImageAnalysis.ImageReaderMode imageReaderMode) {
+        ImageAnalysis imageAnalysis = new ImageAnalysis(new ImageAnalysisConfig.Builder()
+                .setCallbackHandler(mCallbackHandler)
+                .setBackgroundExecutor(mBackgroundExecutor)
+                .setImageQueueDepth(QUEUE_DEPTH)
+                .setImageReaderMode(imageReaderMode)
+                .build());
+
+        imageAnalysis.setAnalyzer(new ImageAnalysis.Analyzer() {
+            @Override
+            public void analyze(ImageProxy image, int rotationDegrees) {
+                mImagesReceived.add(image.getImage());
+            }
+        });
+
+        Map<String, Size> suggestedResolutionMap = new HashMap<>();
+        suggestedResolutionMap.put(ShadowCameraX.DEFAULT_CAMERA_ID, DEFAULT_RESOLUTION);
+        imageAnalysis.updateSuggestedResolution(suggestedResolutionMap);
+    }
+
+    /**
+     * Flushes a {@link Handler} to run all pending tasks.
+     *
+     * @param handler the {@link Handler} to flush.
+     */
+    private static void flushHandler(Handler handler) {
+        ((ShadowLooper) Shadow.extract(handler.getLooper())).idle();
+    }
+
+    private static Image createMockImage(long timestamp) {
+        Image mockImage = mock(Image.class);
+        when(mockImage.getTimestamp()).thenReturn(timestamp);
+        return mockImage;
+    }
+}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ShadowCameraX.java b/camera/camera-core/src/test/java/androidx/camera/core/ShadowCameraX.java
new file mode 100644
index 0000000..fd893b4
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ShadowCameraX.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+/**
+ * A Robolectric shadow of {@link CameraX}.
+ */
+@Implements(CameraX.class)
+public class ShadowCameraX {
+    public static final String DEFAULT_CAMERA_ID = "DEFAULT_CAMERA_ID";
+
+    private static final UseCaseConfig<ImageAnalysis> DEFAULT_IMAGE_ANALYSIS_CONFIG =
+            new ImageAnalysisConfig.Builder().setSessionOptionUnpacker(
+                    new SessionConfig.OptionUnpacker() {
+                        @Override
+                        public void unpack(UseCaseConfig<?> config, SessionConfig.Builder builder) {
+                            // no op.
+                        }
+                    }).build();
+
+    private static final CameraInfo DEFAULT_CAMERA_INFO = new CameraInfo() {
+
+        @Override
+        public CameraX.LensFacing getLensFacing() {
+            return CameraX.LensFacing.BACK;
+        }
+
+        @Override
+        public int getSensorRotationDegrees() {
+            return 0;
+        }
+
+        @Override
+        public int getSensorRotationDegrees(int relativeRotation) {
+            return 0;
+        }
+    };
+
+    /**
+     * Shadow of {@link ShadowCameraX#getCameraWithCameraDeviceConfig(CameraDeviceConfig)}.
+     */
+    @Implementation
+    public static String getCameraWithCameraDeviceConfig(CameraDeviceConfig config) {
+        return DEFAULT_CAMERA_ID;
+    }
+
+    /**
+     * Shadow of {@link CameraX#getCameraInfo(String)}.
+     */
+    @Implementation
+    public static CameraInfo getCameraInfo(String cameraId) throws CameraInfoUnavailableException {
+        return DEFAULT_CAMERA_INFO;
+    }
+
+    /**
+     * Shadow of {@link CameraX#getDefaultUseCaseConfig(Class, CameraX.LensFacing)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Implementation
+    public static <C extends UseCaseConfig<?>> C getDefaultUseCaseConfig(
+            Class<C> configType, CameraX.LensFacing lensFacing) {
+        return (C) DEFAULT_IMAGE_ANALYSIS_CONFIG;
+    }
+
+}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ShadowImageReader.java b/camera/camera-core/src/test/java/androidx/camera/core/ShadowImageReader.java
new file mode 100644
index 0000000..40b067d
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ShadowImageReader.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core;
+
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Build;
+import android.os.Handler;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
+
+/**
+ * A Robolectric shadow of {@link ImageReader}.
+ */
+@Implements(ImageReader.class)
+public class ShadowImageReader {
+
+    // Image to return when user call acquireLatestImage() or acquireNextImage().
+    private static Image sIncomingImage;
+
+    @Nullable
+    private volatile ImageReader.OnImageAvailableListener mListener;
+
+    private static ImageReader sImageReader;
+    private static ShadowImageReader sShadowImageReader;
+
+    /**
+     * Shadow of {@link ImageReader#newInstance(int, int, int, int)}.
+     */
+    @Implementation
+    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+    public static ImageReader newInstance(int width, int height, int format, int maxImages) {
+        sImageReader = Shadow.newInstanceOf(ImageReader.class);
+        sShadowImageReader = Shadow.extract(sImageReader);
+        return sImageReader;
+    }
+
+    /**
+     * Sets the incoming image and triggers
+     * {@link ImageReader.OnImageAvailableListener#onImageAvailable(ImageReader)}.
+     */
+    public static void triggerCallbackWithImage(Image mockImage) {
+        sIncomingImage = mockImage;
+        sShadowImageReader.getListener().onImageAvailable(sImageReader);
+    }
+
+    /**
+     * Clears incoming images.
+     */
+    public static void clear() {
+        sIncomingImage = null;
+        sImageReader = null;
+        sShadowImageReader = null;
+    }
+
+
+    /**
+     * Shadow of {@link ImageReader#setOnImageAvailableListener}.
+     */
+    @Implementation
+    public void setOnImageAvailableListener(ImageReader.OnImageAvailableListener listener,
+            Handler handler) {
+        this.mListener = listener;
+    }
+
+    /**
+     * Shadow of {@link ImageReader#acquireLatestImage()}.
+     */
+    @Implementation
+    public Image acquireLatestImage() {
+        return popIncomingImage();
+    }
+
+    /**
+     * Shadow of {@link ImageReader#acquireNextImage()}.
+     */
+    @Implementation
+    public Image acquireNextImage() {
+        return popIncomingImage();
+    }
+
+    private Image popIncomingImage() {
+        try {
+            return sIncomingImage;
+        } finally {
+            sIncomingImage = null;
+        }
+    }
+
+    /**
+     * Returns the last OnImageAvailableListener that was passed in call to
+     * setOnImageAvailableListener or null if never called.
+     */
+    @Nullable
+    public ImageReader.OnImageAvailableListener getListener() {
+        return mListener;
+    }
+}
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsErrorListenerTest.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsErrorListenerTest.java
index e45ab82..1cc603b 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsErrorListenerTest.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsErrorListenerTest.java
@@ -24,6 +24,7 @@
 
 import android.Manifest;
 import android.content.Context;
+import android.os.Build;
 
 import androidx.annotation.NonNull;
 import androidx.camera.core.AppConfig;
@@ -96,6 +97,10 @@
 
     @Before
     public void setUp() {
+        // Ignore the tests if SDK is before M since extension implementation is only supported
+        // after M.
+        assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
+
         Context context = ApplicationProvider.getApplicationContext();
         CameraDeviceSurfaceManager surfaceManager = new FakeCameraDeviceSurfaceManager();
         ExtendableUseCaseConfigFactory defaultConfigFactory = new ExtendableUseCaseConfigFactory();
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/CameraUtil.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/CameraUtil.java
index b85eb35..b0898c7 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/CameraUtil.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/CameraUtil.java
@@ -22,10 +22,14 @@
 import android.hardware.camera2.CameraManager;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.camera.core.CameraDeviceConfig;
 import androidx.camera.core.CameraInfoUnavailableException;
 import androidx.camera.core.CameraX;
+import androidx.camera.core.LensFacingCameraIdFilter;
+
+import java.util.Set;
 
 /**
  * Utility functions for accessing camera related parameters
@@ -44,6 +48,17 @@
         }
     }
 
+    @NonNull
+    static Set<String> getCameraIdSetWithLensFacing(CameraX.LensFacing lensFacing)
+            throws CameraInfoUnavailableException {
+        Set<String> availableCameraIds = CameraX.getCameraFactory().getAvailableCameraIds();
+        LensFacingCameraIdFilter lensFacingCameraIdFilter =
+                LensFacingCameraIdFilter.createLensFacingCameraIdFilter(lensFacing);
+        availableCameraIds = lensFacingCameraIdFilter.filter(availableCameraIds);
+
+        return availableCameraIds;
+    }
+
     static CameraCharacteristics getCameraCharacteristics(String cameraId) {
         Context context = CameraX.getContext();
         CameraManager cameraManager = (CameraManager) context.getSystemService(
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
index 4e1613a..64378b6 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
@@ -105,22 +105,23 @@
             LensFacing lensFacing) {
         ImageCaptureConfig.Builder builder = new ImageCaptureConfig.Builder();
         builder.setLensFacing(lensFacing);
+        ImageCaptureExtender extender;
 
         switch (effectMode) {
             case BOKEH:
-                BokehImageCaptureExtender.create(builder);
+                extender = BokehImageCaptureExtender.create(builder);
                 break;
             case HDR:
-                HdrImageCaptureExtender.create(builder);
+                extender = HdrImageCaptureExtender.create(builder);
                 break;
             case NIGHT:
-                NightImageCaptureExtender.create(builder);
+                extender = NightImageCaptureExtender.create(builder);
                 break;
             case BEAUTY:
-                BeautyImageCaptureExtender.create(builder);
+                extender = BeautyImageCaptureExtender.create(builder);
                 break;
             case AUTO:
-                AutoImageCaptureExtender.create(builder);
+                extender = AutoImageCaptureExtender.create(builder);
                 break;
             case NORMAL:
                 return true;
@@ -128,9 +129,7 @@
                 return false;
         }
 
-        String cameraId = CameraUtil.getCameraId(builder.build());
-
-        return cameraId != null;
+        return extender.isExtensionAvailable();
     }
 
     /**
@@ -163,22 +162,23 @@
             LensFacing lensFacing) {
         PreviewConfig.Builder builder = new PreviewConfig.Builder();
         builder.setLensFacing(lensFacing);
+        PreviewExtender extender;
 
         switch (effectMode) {
             case BOKEH:
-                BokehPreviewExtender.create(builder);
+                extender = BokehPreviewExtender.create(builder);
                 break;
             case HDR:
-                HdrPreviewExtender.create(builder);
+                extender = HdrPreviewExtender.create(builder);
                 break;
             case NIGHT:
-                NightPreviewExtender.create(builder);
+                extender = NightPreviewExtender.create(builder);
                 break;
             case BEAUTY:
-                BeautyPreviewExtender.create(builder);
+                extender = BeautyPreviewExtender.create(builder);
                 break;
             case AUTO:
-                AutoPreviewExtender.create(builder);
+                extender = AutoPreviewExtender.create(builder);
                 break;
             case NORMAL:
                 return true;
@@ -186,9 +186,7 @@
                 return false;
         }
 
-        String cameraId = CameraUtil.getCameraId(builder.build());
-
-        return cameraId != null;
+        return extender.isExtensionAvailable();
     }
 
     private ExtensionsManager() {
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java
index e4d59ec..2d0e31e 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ImageCaptureExtender.java
@@ -27,6 +27,7 @@
 import androidx.camera.camera2.impl.CameraEventCallbacks;
 import androidx.camera.core.CameraIdFilter;
 import androidx.camera.core.CameraIdFilterSet;
+import androidx.camera.core.CameraInfoUnavailableException;
 import androidx.camera.core.CameraX;
 import androidx.camera.core.CaptureBundle;
 import androidx.camera.core.CaptureConfig;
@@ -43,6 +44,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -61,17 +63,6 @@
         mBuilder = builder;
         mImpl = implementation;
         mEffectMode = effectMode;
-
-        ExtensionCameraIdFilter extensionCameraIdFilter = new ExtensionCameraIdFilter(mImpl);
-        CameraIdFilter cameraIdFilter = mBuilder.build().getCameraIdFilter(null);
-        if (cameraIdFilter == null) {
-            mBuilder.setCameraIdFilter(extensionCameraIdFilter);
-        } else {
-            CameraIdFilterSet cameraIdFilterSet = new CameraIdFilterSet();
-            cameraIdFilterSet.addCameraIdFilter(cameraIdFilter);
-            cameraIdFilterSet.addCameraIdFilter(extensionCameraIdFilter);
-            mBuilder.setCameraIdFilter(cameraIdFilterSet);
-        }
     }
 
     /**
@@ -80,8 +71,18 @@
      * @return True if the specific extension function is supported for the camera device.
      */
     public boolean isExtensionAvailable() {
-        String cameraId = CameraUtil.getCameraId(mBuilder.build());
-        return cameraId != null;
+        CameraX.LensFacing lensFacing = mBuilder.build().getLensFacing();
+        Set<String> availableCameraIds = null;
+        try {
+            availableCameraIds = CameraUtil.getCameraIdSetWithLensFacing(lensFacing);
+        } catch (CameraInfoUnavailableException e) {
+            // Returns false if camera info is unavailable.
+            return false;
+        }
+        ExtensionCameraIdFilter extensionCameraIdFilter = new ExtensionCameraIdFilter(mImpl);
+        availableCameraIds = extensionCameraIdFilter.filter(availableCameraIds);
+
+        return !availableCameraIds.isEmpty();
     }
 
     /**
@@ -92,6 +93,18 @@
      * enabled together.
      */
     public void enableExtension() {
+        // Add extension camera id filter to config.
+        ExtensionCameraIdFilter extensionCameraIdFilter = new ExtensionCameraIdFilter(mImpl);
+        CameraIdFilter currentCameraIdFilter = mBuilder.build().getCameraIdFilter(null);
+        if (currentCameraIdFilter == null) {
+            mBuilder.setCameraIdFilter(extensionCameraIdFilter);
+        } else {
+            CameraIdFilterSet cameraIdFilterSet = new CameraIdFilterSet();
+            cameraIdFilterSet.addCameraIdFilter(currentCameraIdFilter);
+            cameraIdFilterSet.addCameraIdFilter(extensionCameraIdFilter);
+            mBuilder.setCameraIdFilter(cameraIdFilterSet);
+        }
+
         String cameraId = CameraUtil.getCameraId(mBuilder.build());
         if (cameraId == null) {
             // If there's no available camera id for the extender to function, just return here
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java
index 955cbd9..6f9beb4 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/PreviewExtender.java
@@ -32,6 +32,7 @@
 import androidx.camera.core.CameraCaptureResults;
 import androidx.camera.core.CameraIdFilter;
 import androidx.camera.core.CameraIdFilterSet;
+import androidx.camera.core.CameraInfoUnavailableException;
 import androidx.camera.core.CameraX;
 import androidx.camera.core.CaptureConfig;
 import androidx.camera.core.CaptureStage;
@@ -48,6 +49,7 @@
 import androidx.camera.extensions.impl.RequestUpdateProcessorImpl;
 
 import java.util.Collection;
+import java.util.Set;
 
 /**
  * Class for using an OEM provided extension on preview.
@@ -65,17 +67,6 @@
         mBuilder = builder;
         mImpl = implementation;
         mEffectMode = effectMode;
-
-        ExtensionCameraIdFilter extensionCameraIdFilter = new ExtensionCameraIdFilter(mImpl);
-        CameraIdFilter cameraIdFilter = mBuilder.build().getCameraIdFilter(null);
-        if (cameraIdFilter == null) {
-            mBuilder.setCameraIdFilter(extensionCameraIdFilter);
-        } else {
-            CameraIdFilterSet cameraIdFilterSet = new CameraIdFilterSet();
-            cameraIdFilterSet.addCameraIdFilter(cameraIdFilter);
-            cameraIdFilterSet.addCameraIdFilter(extensionCameraIdFilter);
-            mBuilder.setCameraIdFilter(cameraIdFilterSet);
-        }
     }
 
     /**
@@ -85,8 +76,18 @@
      * @return True if the specific extension function is supported for the camera device.
      */
     public boolean isExtensionAvailable() {
-        String cameraId = CameraUtil.getCameraId(mBuilder.build());
-        return cameraId != null;
+        CameraX.LensFacing lensFacing = mBuilder.build().getLensFacing();
+        Set<String> availableCameraIds = null;
+        try {
+            availableCameraIds = CameraUtil.getCameraIdSetWithLensFacing(lensFacing);
+        } catch (CameraInfoUnavailableException e) {
+            // Returns false if camera info is unavailable.
+            return false;
+        }
+        ExtensionCameraIdFilter extensionCameraIdFilter = new ExtensionCameraIdFilter(mImpl);
+        availableCameraIds = extensionCameraIdFilter.filter(availableCameraIds);
+
+        return !availableCameraIds.isEmpty();
     }
 
     /**
@@ -97,6 +98,18 @@
      * extension is not enabled together.
      */
     public void enableExtension() {
+        // Add extension camera id filter to config.
+        ExtensionCameraIdFilter extensionCameraIdFilter = new ExtensionCameraIdFilter(mImpl);
+        CameraIdFilter currentCameraIdFilter = mBuilder.build().getCameraIdFilter(null);
+        if (currentCameraIdFilter == null) {
+            mBuilder.setCameraIdFilter(extensionCameraIdFilter);
+        } else {
+            CameraIdFilterSet cameraIdFilterSet = new CameraIdFilterSet();
+            cameraIdFilterSet.addCameraIdFilter(currentCameraIdFilter);
+            cameraIdFilterSet.addCameraIdFilter(extensionCameraIdFilter);
+            mBuilder.setCameraIdFilter(cameraIdFilterSet);
+        }
+
         String cameraId = CameraUtil.getCameraId(mBuilder.build());
         if (cameraId == null) {
             // If there's no available camera id for the extender to function, just return here
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ToggleButtonTest.java b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ToggleButtonTest.java
index 1e65e192..8fe00cf 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ToggleButtonTest.java
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/ToggleButtonTest.java
@@ -53,10 +53,11 @@
 public final class ToggleButtonTest {
 
     private static final int DISMISS_LOCK_SCREEN_CODE = 82;
+    private static final String BASIC_SAMPLE_PACKAGE = "androidx.camera.integration.extensions";
 
     @Rule
     public ActivityTestRule<CameraExtensionsActivity> mActivityRule =
-            new ActivityTestRule<>(CameraExtensionsActivity.class);
+            new ActivityTestRule<>(CameraExtensionsActivity.class, true, false);
 
     @Rule
     public GrantPermissionRule mCameraPermissionRule =
@@ -85,6 +86,11 @@
         // Close system dialogs first to avoid interrupt.
         ApplicationProvider.getApplicationContext().sendBroadcast(
                 new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+
+        Intent intent = ApplicationProvider.getApplicationContext().getPackageManager()
+                .getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE);
+
+        mActivityRule.launchActivity(intent);
     }
 
     @After
diff --git a/camera/integration-tests/extensionstestapp/src/main/AndroidManifest.xml b/camera/integration-tests/extensionstestapp/src/main/AndroidManifest.xml
index f7ceba1..4916933 100644
--- a/camera/integration-tests/extensionstestapp/src/main/AndroidManifest.xml
+++ b/camera/integration-tests/extensionstestapp/src/main/AndroidManifest.xml
@@ -20,10 +20,6 @@
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
-    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
-    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
-    <uses-permission android:name="android.permission.WRITE_MEDIA_IMAGES" />
-    <uses-permission android:name="android.permission.WRITE_MEDIA_VIDEO" />
 
     <uses-feature android:name="android.hardware.camera" />
 
diff --git a/car/core/api/api_lint.ignore b/car/core/api/api_lint.ignore
index ce9444c..5a9daf1 100644
--- a/car/core/api/api_lint.ignore
+++ b/car/core/api/api_lint.ignore
@@ -1,5 +1,3 @@
 // Baseline format: 1.0
 KotlinOperator: androidx.car.widget.ListItemProvider#get(int):
     Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: androidx.car.widget.ListItemProvider.ListProvider#get(int):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
diff --git a/collection/collection/api/api_lint.ignore b/collection/collection/api/api_lint.ignore
index 589b899..c01fd32 100644
--- a/collection/collection/api/api_lint.ignore
+++ b/collection/collection/api/api_lint.ignore
@@ -1,6 +1,4 @@
 // Baseline format: 1.0
-KotlinOperator: androidx.collection.ArraySet#contains(Object):
-    Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
 KotlinOperator: androidx.collection.CircularArray#get(int):
     Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
 KotlinOperator: androidx.collection.CircularIntArray#get(int):
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxTypeResolutionTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxTypeResolutionTests.kt
index 1974f9d..e4bb666 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxTypeResolutionTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/KtxTypeResolutionTests.kt
@@ -160,10 +160,10 @@
 
 
             @Composable fun test(
-                @Children children: @Composable() () -> Unit,
+                children: @Composable() () -> Unit,
                 value: Int,
                 x: Int,
-                @Children children2: @Composable() () -> Unit,
+                children2: @Composable() () -> Unit,
                 value2: Int
             ) {
                 <LinearLayout>
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ChildrenAnnotationTest.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ChildrenAnnotationTest.kt
new file mode 100644
index 0000000..dedcd26
--- /dev/null
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/analysis/ChildrenAnnotationTest.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.plugins.kotlin.analysis
+
+import androidx.compose.plugins.kotlin.AbstractComposeDiagnosticsTest
+
+class ChildrenAnnotationTest : AbstractComposeDiagnosticsTest() {
+
+    fun testReportChildrenOnWrongParameter() {
+        doTest("""
+            import androidx.compose.*;
+
+            @Composable fun MyWidget(<!CHILDREN_MUST_BE_LAST!>@Children children: ()->Unit<!>, value: Int) {
+                System.out.println(""+children+value)
+            }
+        """)
+    }
+}
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ChildAnnotationChecker.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ChildAnnotationChecker.kt
new file mode 100644
index 0000000..c618a31
--- /dev/null
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ChildAnnotationChecker.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.plugins.kotlin
+
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtElement
+import androidx.compose.plugins.kotlin.analysis.ComposeErrors
+import org.jetbrains.kotlin.container.StorageComponentContainer
+import org.jetbrains.kotlin.container.useInstance
+import org.jetbrains.kotlin.descriptors.ModuleDescriptor
+import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
+import org.jetbrains.kotlin.resolve.TargetPlatform
+import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
+import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
+import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
+
+open class ChildAnnotationChecker() : DeclarationChecker, StorageComponentContainerContributor {
+    override fun check(
+        declaration: KtDeclaration,
+        descriptor: DeclarationDescriptor,
+        context: DeclarationCheckerContext
+    ) {
+        if(descriptor is FunctionDescriptor) {
+            descriptor.valueParameters.forEachIndexed { index, param ->
+                if(param.hasChildrenAnnotation() && index != descriptor.valueParameters.lastIndex) {
+                    context.trace.report(ComposeErrors.CHILDREN_MUST_BE_LAST.on(param.findPsi() as KtElement))
+                }
+            }
+        }
+    }
+
+    override fun registerModuleComponents(
+        container: StorageComponentContainer,
+        platform: TargetPlatform,
+        moduleDescriptor: ModuleDescriptor
+    ) {
+        if (platform != JvmPlatform) return
+        container.useInstance(this)
+    }
+}
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt
index e046b68c..bab14b9 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposePlugin.kt
@@ -111,6 +111,10 @@
             )
             StorageComponentContainerContributor.registerExtension(
                 project,
+                ChildAnnotationChecker()
+            )
+            StorageComponentContainerContributor.registerExtension(
+                project,
                 UnionAnnotationCheckerProvider()
             )
             KtxParsingExtension.registerExtension(project,
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ResolvedKtxElementCall.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ResolvedKtxElementCall.kt
index d5e819f..ed88c3c 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ResolvedKtxElementCall.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ResolvedKtxElementCall.kt
@@ -238,13 +238,13 @@
         collector: MutableMap<String, MutableList<AttributeMeta>>
     ) {
         callDescriptor?.let {
-            it.valueParameters.forEach { param ->
+            it.valueParameters.forEachIndexed { index, param ->
                 collector.multiPut(
                     AttributeMeta(
                         name = param.name.asString(),
                         type = param.type,
                         descriptor = param,
-                        isChildren = param.hasChildrenAnnotation()
+                        isChildren = param.hasChildrenAnnotation() || it.valueParameters.size-1 == index
                     )
                 )
             }
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeDefaultErrorMessages.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeDefaultErrorMessages.kt
index 68acda7..ced33ab 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeDefaultErrorMessages.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeDefaultErrorMessages.kt
@@ -157,5 +157,10 @@
             "Ambiguous targets. {0}",
             Renderers.AMBIGUOUS_CALLS
         )
+        MAP.put(
+            ComposeErrors.CHILDREN_MUST_BE_LAST,
+            "Children annotation must only occur on last parameter.  This annotation is deprecated (move children to " +
+                    "be last parameter, make it @Composable, and remove the @Children annotation.)."
+        )
     }
 }
\ No newline at end of file
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeErrors.java b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeErrors.java
index 467ab57..a9b2802 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeErrors.java
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeErrors.java
@@ -94,6 +94,8 @@
             MISSING_REQUIRED_CHILDREN = DiagnosticFactory1.create(ERROR);
     DiagnosticFactory2<KtExpression, Collection<KotlinType>, Collection<KotlinType>>
             ILLEGAL_ASSIGN_TO_UNIONTYPE = DiagnosticFactory2.create(ERROR);
+    DiagnosticFactory0<KtElement>
+            CHILDREN_MUST_BE_LAST = DiagnosticFactory0.create(ERROR);
 
     @SuppressWarnings("UnusedDeclaration")
     Object INITIALIZER = new Object() {
diff --git a/compose/compose-runtime/compose-runtime-benchmark/build.gradle b/compose/compose-runtime/compose-runtime-benchmark/build.gradle
index dcb32f6..22c1201 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/build.gradle
+++ b/compose/compose-runtime/compose-runtime-benchmark/build.gradle
@@ -30,6 +30,10 @@
 android {
     defaultConfig {
         minSdkVersion 21
+
+        // Work-around for setting the testBuildType to "release" for benchmark projects causes gradle import get
+        // confused.
+        testBuildType = "debug"
     }
     lintOptions {
         disable("SyntheticAccessor")
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml
index 4bba238..e48051b 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/AndroidManifest.xml
@@ -18,8 +18,12 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     package="androidx.compose.benchmark">
-    <!-- Important: disable debuggable for accurate performance results -->
+    <!--
+      ~ Important: disable debuggable for accurate performance results
+      ~ requestLegacyExternalStorage to enable legacy JSON reporting when targeting Q
+      -->
     <application
+        android:requestLegacyExternalStorage="true"
         android:debuggable="false"
         tools:replace="android:debuggable">
         <activity
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmarkBase.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmarkBase.kt
index 8d72e02..29fd820 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmarkBase.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmarkBase.kt
@@ -37,17 +37,17 @@
     val activityRule = ActivityTestRule(ComposeActivity::class.java)
 
     fun measureCompose(block: @Composable() () -> Unit) {
+        val activity = activityRule.activity
         benchmarkRule.measureRepeated {
-            val activity = activityRule.activity
-
             activity.setContent {
                 block()
             }
 
             runWithTimingDisabled {
-                activity.disposeComposition()
+                activity.setContent { }
             }
         }
+        activity.disposeComposition()
     }
 
     fun measureRecompose(block: RecomposeReceiver.() -> Unit) {
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
index 6525704..da720b8 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
@@ -73,7 +73,7 @@
 }
 
 @Composable
-fun Table(@Children children: @Composable() () -> Unit) {
+fun Table(children: @Composable() () -> Unit) {
     Column { children() }
 }
 
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt
index 7712cee..c7667bc 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt
@@ -36,7 +36,7 @@
 }
 
 @Composable
-fun Stack(vertical: Boolean, @Children children: @Composable() () -> Unit) {
+fun Stack(vertical: Boolean, children: @Composable() () -> Unit) {
     if (vertical) {
         Column { children() }
     } else {
@@ -45,7 +45,7 @@
 }
 
 @Composable
-fun Container(@Children children: @Composable() () -> Unit) {
+fun Container(children: @Composable() () -> Unit) {
     // non-layout node component. just adds depth to the composition hierarchy.
     children()
 }
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt
index 6b1d4b7..f91235e 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt
@@ -31,7 +31,7 @@
 import kotlin.random.Random
 
 @Composable
-fun Stack(@Children children: @Composable() () -> Unit) {
+fun Stack(children: @Composable() () -> Unit) {
     Column {
         children()
     }
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Ambient.kt b/compose/compose-runtime/src/main/java/androidx/compose/Ambient.kt
index 806b474f..ebb19f5 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Ambient.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/Ambient.kt
@@ -113,8 +113,7 @@
     @Composable
     fun Provider(
         value: T,
-        @Children
-                children: @Composable() () -> Unit
+        children: @Composable() () -> Unit
     ) {
         with(currentComposerNonNull) {
             val holder = +memo {
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Composer.kt b/compose/compose-runtime/src/main/java/androidx/compose/Composer.kt
index e5fb5ec..f92a9e5 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Composer.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/Composer.kt
@@ -834,7 +834,8 @@
         if (action == END_NODE) recordUp()
         recordEnd(action)
 
-        if (slots.inEmpty) {
+        val inserting = slots.inEmpty
+        if (inserting) {
             slots.endEmpty()
             if (!slots.inEmpty) recordOperation { _, slots, _ -> slots.endInsert() }
         }
@@ -847,7 +848,8 @@
         previousPending?.let<Pending, Unit> { previous ->
             // Update the parent count of nodes
             previous.updateNodeCount(pending?.parentKeyInfo, expectedNodeCount)
-            previous.groupIndex++
+            if (!inserting)
+                previous.groupIndex++
         }
         this.pending = previousPending
         this.parentKeyInfo = keyStack.pop()
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Key.kt b/compose/compose-runtime/src/main/java/androidx/compose/Key.kt
index 00c0414..41e9c56 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Key.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/Key.kt
@@ -71,6 +71,6 @@
 @Composable
 @Suppress("PLUGIN_ERROR")
 /* inline */
-fun Key(@Suppress("UNUSED_PARAMETER") @Pivotal key: Any?, @Children children: @Composable() () -> Unit) {
+fun Key(@Suppress("UNUSED_PARAMETER") @Pivotal key: Any?, children: @Composable() () -> Unit) {
     children()
 }
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Observe.kt b/compose/compose-runtime/src/main/java/androidx/compose/Observe.kt
index 3a05644..2be1010 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Observe.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/Observe.kt
@@ -31,7 +31,7 @@
  */
 @Composable
 @Suppress("PLUGIN_ERROR")
-fun Observe(@Children body: @Composable() () -> Unit) =
+fun Observe(body: @Composable() () -> Unit) =
     currentComposerNonNull.let { composer ->
         trace("Compose:Observe") {
             composer.startGroup(observer)
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Recompose.kt b/compose/compose-runtime/src/main/java/androidx/compose/Recompose.kt
index 421d431..fb57067 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Recompose.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/Recompose.kt
@@ -59,7 +59,7 @@
  * @see invalidate
  */
 @Composable
-fun Recompose(@Children body: @Composable() (recompose: () -> Unit) -> Unit) {
+fun Recompose(body: @Composable() (recompose: () -> Unit) -> Unit) {
     val composer = currentComposerNonNull
     val recomposer = RecomposeHelper()
     val callback = composer.startJoin(false) {
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/CompositionTests.kt b/compose/compose-runtime/src/test/java/androidx/compose/CompositionTests.kt
index 6a58b69..32fdd69 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/CompositionTests.kt
+++ b/compose/compose-runtime/src/test/java/androidx/compose/CompositionTests.kt
@@ -210,6 +210,52 @@
         }
     }
 
+    fun testReplace() {
+        var includeA = true
+        fun MockViewComposition.composition() {
+            text("Before")
+            if (includeA) {
+                linear {
+                    text("A")
+                }
+            } else {
+                edit("B")
+            }
+            text("After")
+        }
+        fun MockViewValidator.composition() {
+            text("Before")
+            if (includeA) {
+                linear {
+                    text("A")
+                }
+            } else {
+                edit("B")
+            }
+            text("After")
+        }
+        val composer = compose {
+            composition()
+        }
+        validate(composer.root) {
+            composition()
+        }
+        includeA = false
+        compose(composer) {
+            composition()
+        }
+        validate(composer.root) {
+            composition()
+        }
+        includeA = true
+        compose(composer) {
+            composition()
+        }
+        validate(composer.root) {
+            composition()
+        }
+    }
+
     fun testInsertWithMultipleRoots() {
         val chars = listOf('a', 'b', 'c')
 
diff --git a/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/CoordinatorLayout.java b/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/CoordinatorLayout.java
index 2eb7405..4a30d17 100644
--- a/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/CoordinatorLayout.java
+++ b/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/CoordinatorLayout.java
@@ -246,6 +246,12 @@
 
         setupForInsets();
         super.setOnHierarchyChangeListener(new HierarchyChangeListener());
+
+        if (ViewCompat.getImportantForAccessibility(this)
+                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+            ViewCompat.setImportantForAccessibility(this,
+                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
     }
 
     @Override
diff --git a/core/core/api/1.2.0-alpha03.txt b/core/core/api/1.2.0-alpha03.txt
index f03894b..fe4b834 100644
--- a/core/core/api/1.2.0-alpha03.txt
+++ b/core/core/api/1.2.0-alpha03.txt
@@ -1610,7 +1610,7 @@
   }
 
   public interface Predicate<T> {
-    method public boolean test(T);
+    method public boolean test(T?);
   }
 
   public interface Supplier<T> {
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index f03894b..fe4b834 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -1610,7 +1610,7 @@
   }
 
   public interface Predicate<T> {
-    method public boolean test(T);
+    method public boolean test(T?);
   }
 
   public interface Supplier<T> {
diff --git a/core/core/api/restricted_1.2.0-alpha03.txt b/core/core/api/restricted_1.2.0-alpha03.txt
index 138fb9f..fe706a7 100644
--- a/core/core/api/restricted_1.2.0-alpha03.txt
+++ b/core/core/api/restricted_1.2.0-alpha03.txt
@@ -138,9 +138,10 @@
     method public static void putBinder(android.os.Bundle, String?, android.os.IBinder?);
   }
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ComponentActivity extends android.app.Activity implements androidx.core.view.KeyEventDispatcher.Component {
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ComponentActivity extends android.app.Activity implements androidx.core.view.KeyEventDispatcher.Component androidx.lifecycle.LifecycleOwner {
     ctor public ComponentActivity();
     method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T extends androidx.core.app.ComponentActivity.ExtraData> T! getExtraData(Class<T!>!);
+    method public androidx.lifecycle.Lifecycle getLifecycle();
     method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void putExtraData(androidx.core.app.ComponentActivity.ExtraData!);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean superDispatchKeyEvent(android.view.KeyEvent!);
   }
@@ -1968,7 +1969,7 @@
   }
 
   public interface Predicate<T> {
-    method public boolean test(T);
+    method public boolean test(T?);
   }
 
   public interface Supplier<T> {
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 138fb9f..fe706a7 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -138,9 +138,10 @@
     method public static void putBinder(android.os.Bundle, String?, android.os.IBinder?);
   }
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ComponentActivity extends android.app.Activity implements androidx.core.view.KeyEventDispatcher.Component {
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ComponentActivity extends android.app.Activity implements androidx.core.view.KeyEventDispatcher.Component androidx.lifecycle.LifecycleOwner {
     ctor public ComponentActivity();
     method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T extends androidx.core.app.ComponentActivity.ExtraData> T! getExtraData(Class<T!>!);
+    method public androidx.lifecycle.Lifecycle getLifecycle();
     method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void putExtraData(androidx.core.app.ComponentActivity.ExtraData!);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean superDispatchKeyEvent(android.view.KeyEvent!);
   }
@@ -1968,7 +1969,7 @@
   }
 
   public interface Predicate<T> {
-    method public boolean test(T);
+    method public boolean test(T?);
   }
 
   public interface Supplier<T> {
diff --git a/core/core/src/main/java/androidx/core/app/ComponentActivity.java b/core/core/src/main/java/androidx/core/app/ComponentActivity.java
index 90d61c4..78ebed2 100644
--- a/core/core/src/main/java/androidx/core/app/ComponentActivity.java
+++ b/core/core/src/main/java/androidx/core/app/ComponentActivity.java
@@ -18,13 +18,22 @@
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
 
+import android.annotation.SuppressLint;
 import android.app.Activity;
+import android.os.Bundle;
 import android.view.KeyEvent;
 import android.view.View;
 
+import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.collection.SimpleArrayMap;
 import androidx.core.view.KeyEventDispatcher;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.lifecycle.ReportFragment;
 
 /**
  * Base class for activities that enables composition of higher level components.
@@ -36,8 +45,9 @@
  * @hide
  */
 @RestrictTo(LIBRARY_GROUP_PREFIX)
-public class ComponentActivity extends Activity
-        implements KeyEventDispatcher.Component {
+public class ComponentActivity extends Activity implements
+        LifecycleOwner,
+        KeyEventDispatcher.Component {
     /**
      * Storage for {@link ExtraData} instances.
      *
@@ -46,6 +56,11 @@
     @SuppressWarnings("deprecation")
     private SimpleArrayMap<Class<? extends ExtraData>, ExtraData> mExtraDataMap =
             new SimpleArrayMap<>();
+    /**
+     * This is only used for apps that have not switched to Fragments 1.1.0, where this
+     * behavior is provided by <code>androidx.activity.ComponentActivity</code>.
+     */
+    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
 
     /**
      * Store an instance of {@link ExtraData} for later retrieval by class name
@@ -64,6 +79,20 @@
         mExtraDataMap.put(extraData.getClass(), extraData);
     }
 
+    @SuppressLint("RestrictedApi")
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        ReportFragment.injectIfNeededIn(this);
+    }
+
+    @CallSuper
+    @Override
+    protected void onSaveInstanceState(@NonNull Bundle outState) {
+        mLifecycleRegistry.markState(Lifecycle.State.CREATED);
+        super.onSaveInstanceState(outState);
+    }
+
     /**
      * Retrieves a previously set {@link ExtraData} by class name.
      *
@@ -78,6 +107,12 @@
         return (T) mExtraDataMap.get(extraDataClass);
     }
 
+    @NonNull
+    @Override
+    public Lifecycle getLifecycle() {
+        return mLifecycleRegistry;
+    }
+
     /**
      * @hide
      */
diff --git a/core/core/src/main/java/androidx/core/util/Predicate.java b/core/core/src/main/java/androidx/core/util/Predicate.java
index af73843..41e8582 100644
--- a/core/core/src/main/java/androidx/core/util/Predicate.java
+++ b/core/core/src/main/java/androidx/core/util/Predicate.java
@@ -16,7 +16,7 @@
 
 package androidx.core.util;
 
-import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 /**
  * Compat version of {@link java.util.function.Predicate}
@@ -32,5 +32,5 @@
      * @return {@code true} if the input argument matches the predicate,
      * otherwise {@code false}
      */
-    boolean test(@NonNull T t);
+    boolean test(@Nullable T t);
 }
diff --git a/fragment/fragment-ktx/api/api_lint.ignore b/fragment/fragment-ktx/api/api_lint.ignore
deleted file mode 100644
index cd75382..0000000
--- a/fragment/fragment-ktx/api/api_lint.ignore
+++ /dev/null
@@ -1,3 +0,0 @@
-// Baseline format: 1.0
-DocumentExceptions: androidx.fragment.app.FragmentViewModelLazyKt#createViewModelLazy(androidx.fragment.app.Fragment, kotlin.reflect.KClass<VM>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStore>, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>):
-    Method FragmentViewModelLazyKt.createViewModelLazy appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
diff --git a/gradlew b/gradlew
index 1cf59b6..5e8daf4 100755
--- a/gradlew
+++ b/gradlew
@@ -9,6 +9,8 @@
 # --------- androidx specific code needed for build server. ------------------
 
 if [ -n "$OUT_DIR" ] ; then
+    mkdir -p "$OUT_DIR"
+    OUT_DIR="$(cd $OUT_DIR && pwd)"
     export GRADLE_USER_HOME="$OUT_DIR/.gradle"
     export LINT_PRINT_STACKTRACE=true
 else
diff --git a/leanback/api/api_lint.ignore b/leanback/api/api_lint.ignore
index b8d6059..47e6f64 100644
--- a/leanback/api/api_lint.ignore
+++ b/leanback/api/api_lint.ignore
@@ -49,22 +49,8 @@
     Fractions must use floats, was `androidx.leanback.widget.Parallax.PropertyMarkerValue` in `atFraction`
 
 
-KotlinOperator: androidx.leanback.widget.ArrayObjectAdapter#get(int):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: androidx.leanback.widget.CursorObjectAdapter#get(int):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
 KotlinOperator: androidx.leanback.widget.ObjectAdapter#get(int):
     Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: androidx.leanback.widget.Parallax.FloatProperty#get(androidx.leanback.widget.Parallax):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: androidx.leanback.widget.Parallax.FloatProperty#set(androidx.leanback.widget.Parallax, Float):
-    Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: androidx.leanback.widget.Parallax.IntProperty#get(androidx.leanback.widget.Parallax):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: androidx.leanback.widget.Parallax.IntProperty#set(androidx.leanback.widget.Parallax, Integer):
-    Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: androidx.leanback.widget.SparseArrayObjectAdapter#get(int):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
 KotlinOperator: androidx.leanback.widget.SparseArrayObjectAdapter#set(int, Object):
     Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
 
diff --git a/localbroadcastmanager/src/main/java/androidx/localbroadcastmanager/content/LocalBroadcastManager.java b/localbroadcastmanager/src/main/java/androidx/localbroadcastmanager/content/LocalBroadcastManager.java
index 04f2f7f..8323b05 100644
--- a/localbroadcastmanager/src/main/java/androidx/localbroadcastmanager/content/LocalBroadcastManager.java
+++ b/localbroadcastmanager/src/main/java/androidx/localbroadcastmanager/content/LocalBroadcastManager.java
@@ -212,7 +212,7 @@
      * @see #registerReceiver
      *
      * @return Returns true if the intent has been scheduled for delivery to one or more
-     * broadcast receivers.  (Note tha delivery may not ultimately take place if one of those
+     * broadcast receivers.  (Note that delivery may not ultimately take place if one of those
      * receivers is unregistered before it is dispatched.)
      */
     public boolean sendBroadcast(@NonNull Intent intent) {
diff --git a/media/api/restricted_1.1.0-rc01.txt b/media/api/restricted_1.1.0-rc01.txt
new file mode 100644
index 0000000..b48a150
--- /dev/null
+++ b/media/api/restricted_1.1.0-rc01.txt
@@ -0,0 +1,702 @@
+// Signature format: 3.0
+package android.support.v4.media {
+
+  public final class MediaBrowserCompat {
+    ctor public MediaBrowserCompat(android.content.Context!, android.content.ComponentName!, android.support.v4.media.MediaBrowserCompat.ConnectionCallback!, android.os.Bundle!);
+    method public void connect();
+    method public void disconnect();
+    method public android.os.Bundle? getExtras();
+    method public void getItem(String, android.support.v4.media.MediaBrowserCompat.ItemCallback);
+    method public String getRoot();
+    method public android.content.ComponentName getServiceComponent();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public boolean isConnected();
+    method public void search(String, android.os.Bundle!, android.support.v4.media.MediaBrowserCompat.SearchCallback);
+    method public void sendCustomAction(String, android.os.Bundle!, android.support.v4.media.MediaBrowserCompat.CustomActionCallback?);
+    method public void subscribe(String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void subscribe(String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void unsubscribe(String);
+    method public void unsubscribe(String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    field public static final String CUSTOM_ACTION_DOWNLOAD = "android.support.v4.media.action.DOWNLOAD";
+    field public static final String CUSTOM_ACTION_REMOVE_DOWNLOADED_FILE = "android.support.v4.media.action.REMOVE_DOWNLOADED_FILE";
+    field public static final String EXTRA_DOWNLOAD_PROGRESS = "android.media.browse.extra.DOWNLOAD_PROGRESS";
+    field public static final String EXTRA_MEDIA_ID = "android.media.browse.extra.MEDIA_ID";
+    field public static final String EXTRA_PAGE = "android.media.browse.extra.PAGE";
+    field public static final String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
+  }
+
+  public static class MediaBrowserCompat.ConnectionCallback {
+    ctor public MediaBrowserCompat.ConnectionCallback();
+    method public void onConnected();
+    method public void onConnectionFailed();
+    method public void onConnectionSuspended();
+  }
+
+  public abstract static class MediaBrowserCompat.CustomActionCallback {
+    ctor public MediaBrowserCompat.CustomActionCallback();
+    method public void onError(String!, android.os.Bundle!, android.os.Bundle!);
+    method public void onProgressUpdate(String!, android.os.Bundle!, android.os.Bundle!);
+    method public void onResult(String!, android.os.Bundle!, android.os.Bundle!);
+  }
+
+  public abstract static class MediaBrowserCompat.ItemCallback {
+    ctor public MediaBrowserCompat.ItemCallback();
+    method public void onError(String);
+    method public void onItemLoaded(android.support.v4.media.MediaBrowserCompat.MediaItem!);
+  }
+
+  public static class MediaBrowserCompat.MediaItem implements android.os.Parcelable {
+    ctor public MediaBrowserCompat.MediaItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaBrowserCompat.MediaItem! fromMediaItem(Object!);
+    method public static java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>! fromMediaItemList(java.util.List<?>!);
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public int getFlags();
+    method public String? getMediaId();
+    method public boolean isBrowsable();
+    method public boolean isPlayable();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaBrowserCompat.MediaItem!>! CREATOR;
+    field public static final int FLAG_BROWSABLE = 1; // 0x1
+    field public static final int FLAG_PLAYABLE = 2; // 0x2
+  }
+
+  public abstract static class MediaBrowserCompat.SearchCallback {
+    ctor public MediaBrowserCompat.SearchCallback();
+    method public void onError(String, android.os.Bundle!);
+    method public void onSearchResult(String, android.os.Bundle!, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>);
+  }
+
+  public abstract static class MediaBrowserCompat.SubscriptionCallback {
+    ctor public MediaBrowserCompat.SubscriptionCallback();
+    method public void onChildrenLoaded(String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>);
+    method public void onChildrenLoaded(String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>, android.os.Bundle);
+    method public void onError(String);
+    method public void onError(String, android.os.Bundle);
+  }
+
+  public final class MediaDescriptionCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.MediaDescriptionCompat! fromMediaDescription(Object!);
+    method public CharSequence? getDescription();
+    method public android.os.Bundle? getExtras();
+    method public android.graphics.Bitmap? getIconBitmap();
+    method public android.net.Uri? getIconUri();
+    method public Object! getMediaDescription();
+    method public String? getMediaId();
+    method public android.net.Uri? getMediaUri();
+    method public CharSequence? getSubtitle();
+    method public CharSequence? getTitle();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
+    field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
+    field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
+    field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
+    field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
+    field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
+    field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaDescriptionCompat!>! CREATOR;
+    field public static final String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE";
+    field public static final String EXTRA_DOWNLOAD_STATUS = "android.media.extra.DOWNLOAD_STATUS";
+    field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
+    field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
+    field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
+  }
+
+  public static final class MediaDescriptionCompat.Builder {
+    ctor public MediaDescriptionCompat.Builder();
+    method public android.support.v4.media.MediaDescriptionCompat! build();
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setDescription(CharSequence?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setExtras(android.os.Bundle?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setIconBitmap(android.graphics.Bitmap?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setIconUri(android.net.Uri?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setMediaId(String?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setMediaUri(android.net.Uri?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setSubtitle(CharSequence?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setTitle(CharSequence?);
+  }
+
+  public final class MediaMetadataCompat implements android.os.Parcelable {
+    method public boolean containsKey(String!);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaMetadataCompat! fromMediaMetadata(Object!);
+    method public android.graphics.Bitmap! getBitmap(String!);
+    method public android.os.Bundle! getBundle();
+    method public android.support.v4.media.MediaDescriptionCompat! getDescription();
+    method public long getLong(String!);
+    method public Object! getMediaMetadata();
+    method public android.support.v4.media.RatingCompat! getRating(String!);
+    method public String! getString(String!);
+    method public CharSequence! getText(String!);
+    method public java.util.Set<java.lang.String!>! keySet();
+    method public int size();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaMetadataCompat!>! CREATOR;
+    field public static final String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
+    field public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+    field public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+    field public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+    field public static final String METADATA_KEY_ART = "android.media.metadata.ART";
+    field public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+    field public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
+    field public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+    field public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
+    field public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+    field public static final String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+    field public static final String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+    field public static final String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+    field public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+    field public static final String METADATA_KEY_DOWNLOAD_STATUS = "android.media.metadata.DOWNLOAD_STATUS";
+    field public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+    field public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+    field public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+    field public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+    field public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
+    field public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+    field public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+    field public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public static final class MediaMetadataCompat.Builder {
+    ctor public MediaMetadataCompat.Builder();
+    ctor public MediaMetadataCompat.Builder(android.support.v4.media.MediaMetadataCompat!);
+    method public android.support.v4.media.MediaMetadataCompat! build();
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putBitmap(String!, android.graphics.Bitmap!);
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putLong(String!, long);
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putRating(String!, android.support.v4.media.RatingCompat!);
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putString(String!, String!);
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putText(String!, CharSequence!);
+  }
+
+  public final class RatingCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.RatingCompat! fromRating(Object!);
+    method public float getPercentRating();
+    method public Object! getRating();
+    method public int getRatingStyle();
+    method public float getStarRating();
+    method public boolean hasHeart();
+    method public boolean isRated();
+    method public boolean isThumbUp();
+    method public static android.support.v4.media.RatingCompat! newHeartRating(boolean);
+    method public static android.support.v4.media.RatingCompat! newPercentageRating(float);
+    method public static android.support.v4.media.RatingCompat! newStarRating(int, float);
+    method public static android.support.v4.media.RatingCompat! newThumbRating(boolean);
+    method public static android.support.v4.media.RatingCompat! newUnratedRating(int);
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.RatingCompat!>! CREATOR;
+    field public static final int RATING_3_STARS = 3; // 0x3
+    field public static final int RATING_4_STARS = 4; // 0x4
+    field public static final int RATING_5_STARS = 5; // 0x5
+    field public static final int RATING_HEART = 1; // 0x1
+    field public static final int RATING_NONE = 0; // 0x0
+    field public static final int RATING_PERCENTAGE = 6; // 0x6
+    field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
+  }
+
+}
+
+package android.support.v4.media.session {
+
+  public final class MediaControllerCompat {
+    ctor public MediaControllerCompat(android.content.Context!, android.support.v4.media.session.MediaSessionCompat);
+    ctor public MediaControllerCompat(android.content.Context!, android.support.v4.media.session.MediaSessionCompat.Token) throws android.os.RemoteException;
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat!);
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat!, int);
+    method public void adjustVolume(int, int);
+    method public boolean dispatchMediaButtonEvent(android.view.KeyEvent!);
+    method public android.os.Bundle! getExtras();
+    method public long getFlags();
+    method public static android.support.v4.media.session.MediaControllerCompat! getMediaController(android.app.Activity);
+    method public Object! getMediaController();
+    method public android.support.v4.media.MediaMetadataCompat! getMetadata();
+    method public String! getPackageName();
+    method public android.support.v4.media.session.MediaControllerCompat.PlaybackInfo! getPlaybackInfo();
+    method public android.support.v4.media.session.PlaybackStateCompat! getPlaybackState();
+    method public java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem!>! getQueue();
+    method public CharSequence! getQueueTitle();
+    method public int getRatingType();
+    method public int getRepeatMode();
+    method public android.app.PendingIntent! getSessionActivity();
+    method public android.support.v4.media.session.MediaSessionCompat.Token! getSessionToken();
+    method public int getShuffleMode();
+    method public android.support.v4.media.session.MediaControllerCompat.TransportControls! getTransportControls();
+    method public boolean isCaptioningEnabled();
+    method public boolean isSessionReady();
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback, android.os.Handler!);
+    method public void removeQueueItem(android.support.v4.media.MediaDescriptionCompat!);
+    method @Deprecated public void removeQueueItemAt(int);
+    method public void sendCommand(String, android.os.Bundle?, android.os.ResultReceiver?);
+    method public static void setMediaController(android.app.Activity, android.support.v4.media.session.MediaControllerCompat!);
+    method public void setVolumeTo(int, int);
+    method public void unregisterCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+  }
+
+  public abstract static class MediaControllerCompat.Callback implements android.os.IBinder.DeathRecipient {
+    ctor public MediaControllerCompat.Callback();
+    method public void binderDied();
+    method public void onAudioInfoChanged(android.support.v4.media.session.MediaControllerCompat.PlaybackInfo!);
+    method public void onCaptioningEnabledChanged(boolean);
+    method public void onExtrasChanged(android.os.Bundle!);
+    method public void onMetadataChanged(android.support.v4.media.MediaMetadataCompat!);
+    method public void onPlaybackStateChanged(android.support.v4.media.session.PlaybackStateCompat!);
+    method public void onQueueChanged(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem!>!);
+    method public void onQueueTitleChanged(CharSequence!);
+    method public void onRepeatModeChanged(int);
+    method public void onSessionDestroyed();
+    method public void onSessionEvent(String!, android.os.Bundle!);
+    method public void onSessionReady();
+    method public void onShuffleModeChanged(int);
+  }
+
+  public static final class MediaControllerCompat.PlaybackInfo {
+    method public androidx.media.AudioAttributesCompat getAudioAttributes();
+    method @Deprecated public int getAudioStream();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getPlaybackType();
+    method public int getVolumeControl();
+    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
+  }
+
+  public abstract static class MediaControllerCompat.TransportControls {
+    method public abstract void fastForward();
+    method public abstract void pause();
+    method public abstract void play();
+    method public abstract void playFromMediaId(String!, android.os.Bundle!);
+    method public abstract void playFromSearch(String!, android.os.Bundle!);
+    method public abstract void playFromUri(android.net.Uri!, android.os.Bundle!);
+    method public abstract void prepare();
+    method public abstract void prepareFromMediaId(String!, android.os.Bundle!);
+    method public abstract void prepareFromSearch(String!, android.os.Bundle!);
+    method public abstract void prepareFromUri(android.net.Uri!, android.os.Bundle!);
+    method public abstract void rewind();
+    method public abstract void seekTo(long);
+    method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction!, android.os.Bundle!);
+    method public abstract void sendCustomAction(String!, android.os.Bundle!);
+    method public abstract void setCaptioningEnabled(boolean);
+    method public abstract void setRating(android.support.v4.media.RatingCompat!);
+    method public abstract void setRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
+    method public abstract void setRepeatMode(int);
+    method public abstract void setShuffleMode(int);
+    method public abstract void skipToNext();
+    method public abstract void skipToPrevious();
+    method public abstract void skipToQueueItem(long);
+    method public abstract void stop();
+    field public static final String EXTRA_LEGACY_STREAM_TYPE = "android.media.session.extra.LEGACY_STREAM_TYPE";
+  }
+
+  public class MediaSessionCompat {
+    ctor public MediaSessionCompat(android.content.Context, String);
+    ctor public MediaSessionCompat(android.content.Context, String, android.content.ComponentName?, android.app.PendingIntent?);
+    method public void addOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener!);
+    method public static android.support.v4.media.session.MediaSessionCompat! fromMediaSession(android.content.Context!, Object!);
+    method public android.support.v4.media.session.MediaControllerCompat! getController();
+    method public final androidx.media.MediaSessionManager.RemoteUserInfo getCurrentControllerInfo();
+    method public Object! getMediaSession();
+    method public Object! getRemoteControlClient();
+    method public android.support.v4.media.session.MediaSessionCompat.Token! getSessionToken();
+    method public boolean isActive();
+    method public void release();
+    method public void removeOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener!);
+    method public void sendSessionEvent(String!, android.os.Bundle!);
+    method public void setActive(boolean);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback!);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback!, android.os.Handler!);
+    method public void setCaptioningEnabled(boolean);
+    method public void setExtras(android.os.Bundle!);
+    method public void setFlags(int);
+    method public void setMediaButtonReceiver(android.app.PendingIntent!);
+    method public void setMetadata(android.support.v4.media.MediaMetadataCompat!);
+    method public void setPlaybackState(android.support.v4.media.session.PlaybackStateCompat!);
+    method public void setPlaybackToLocal(int);
+    method public void setPlaybackToRemote(androidx.media.VolumeProviderCompat!);
+    method public void setQueue(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem!>!);
+    method public void setQueueTitle(CharSequence!);
+    method public void setRatingType(int);
+    method public void setRepeatMode(int);
+    method public void setSessionActivity(android.app.PendingIntent!);
+    method public void setShuffleMode(int);
+    field public static final String ACTION_FLAG_AS_INAPPROPRIATE = "android.support.v4.media.session.action.FLAG_AS_INAPPROPRIATE";
+    field public static final String ACTION_FOLLOW = "android.support.v4.media.session.action.FOLLOW";
+    field public static final String ACTION_SKIP_AD = "android.support.v4.media.session.action.SKIP_AD";
+    field public static final String ACTION_UNFOLLOW = "android.support.v4.media.session.action.UNFOLLOW";
+    field public static final String ARGUMENT_MEDIA_ATTRIBUTE = "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE";
+    field public static final String ARGUMENT_MEDIA_ATTRIBUTE_VALUE = "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE_VALUE";
+    field @Deprecated public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
+    field @Deprecated public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+    field public static final int MEDIA_ATTRIBUTE_ALBUM = 1; // 0x1
+    field public static final int MEDIA_ATTRIBUTE_ARTIST = 0; // 0x0
+    field public static final int MEDIA_ATTRIBUTE_PLAYLIST = 2; // 0x2
+  }
+
+  public abstract static class MediaSessionCompat.Callback {
+    ctor public MediaSessionCompat.Callback();
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat!);
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat!, int);
+    method public void onCommand(String!, android.os.Bundle!, android.os.ResultReceiver!);
+    method public void onCustomAction(String!, android.os.Bundle!);
+    method public void onFastForward();
+    method public boolean onMediaButtonEvent(android.content.Intent!);
+    method public void onPause();
+    method public void onPlay();
+    method public void onPlayFromMediaId(String!, android.os.Bundle!);
+    method public void onPlayFromSearch(String!, android.os.Bundle!);
+    method public void onPlayFromUri(android.net.Uri!, android.os.Bundle!);
+    method public void onPrepare();
+    method public void onPrepareFromMediaId(String!, android.os.Bundle!);
+    method public void onPrepareFromSearch(String!, android.os.Bundle!);
+    method public void onPrepareFromUri(android.net.Uri!, android.os.Bundle!);
+    method public void onRemoveQueueItem(android.support.v4.media.MediaDescriptionCompat!);
+    method @Deprecated public void onRemoveQueueItemAt(int);
+    method public void onRewind();
+    method public void onSeekTo(long);
+    method public void onSetCaptioningEnabled(boolean);
+    method public void onSetRating(android.support.v4.media.RatingCompat!);
+    method public void onSetRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
+    method public void onSetRepeatMode(int);
+    method public void onSetShuffleMode(int);
+    method public void onSkipToNext();
+    method public void onSkipToPrevious();
+    method public void onSkipToQueueItem(long);
+    method public void onStop();
+  }
+
+  public static interface MediaSessionCompat.OnActiveChangeListener {
+    method public void onActiveChanged();
+  }
+
+  public static final class MediaSessionCompat.QueueItem implements android.os.Parcelable {
+    ctor public MediaSessionCompat.QueueItem(android.support.v4.media.MediaDescriptionCompat!, long);
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.QueueItem! fromQueueItem(Object!);
+    method public static java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem!>! fromQueueItemList(java.util.List<?>!);
+    method public android.support.v4.media.MediaDescriptionCompat! getDescription();
+    method public long getQueueId();
+    method public Object! getQueueItem();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.QueueItem!>! CREATOR;
+    field public static final int UNKNOWN_ID = -1; // 0xffffffff
+  }
+
+  public static final class MediaSessionCompat.Token implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.Token! fromToken(Object!);
+    method public Object! getToken();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.Token!>! CREATOR;
+  }
+
+  public class ParcelableVolumeInfo implements android.os.Parcelable {
+    ctor public ParcelableVolumeInfo(int, int, int, int, int);
+    ctor public ParcelableVolumeInfo(android.os.Parcel!);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.ParcelableVolumeInfo!>! CREATOR;
+    field public int audioStream;
+    field public int controlType;
+    field public int currentVolume;
+    field public int maxVolume;
+    field public int volumeType;
+  }
+
+  public final class PlaybackStateCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat! fromPlaybackState(Object!);
+    method public long getActions();
+    method public long getActiveQueueItemId();
+    method public long getBufferedPosition();
+    method public java.util.List<android.support.v4.media.session.PlaybackStateCompat.CustomAction!>! getCustomActions();
+    method public int getErrorCode();
+    method public CharSequence! getErrorMessage();
+    method public android.os.Bundle? getExtras();
+    method public long getLastPositionUpdateTime();
+    method public float getPlaybackSpeed();
+    method public Object! getPlaybackState();
+    method public long getPosition();
+    method public int getState();
+    method public static int toKeyCode(long);
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
+    field public static final long ACTION_PAUSE = 2L; // 0x2L
+    field public static final long ACTION_PLAY = 4L; // 0x4L
+    field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
+    field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
+    field public static final long ACTION_PLAY_FROM_URI = 8192L; // 0x2000L
+    field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
+    field public static final long ACTION_PREPARE = 16384L; // 0x4000L
+    field public static final long ACTION_PREPARE_FROM_MEDIA_ID = 32768L; // 0x8000L
+    field public static final long ACTION_PREPARE_FROM_SEARCH = 65536L; // 0x10000L
+    field public static final long ACTION_PREPARE_FROM_URI = 131072L; // 0x20000L
+    field public static final long ACTION_REWIND = 8L; // 0x8L
+    field public static final long ACTION_SEEK_TO = 256L; // 0x100L
+    field public static final long ACTION_SET_CAPTIONING_ENABLED = 1048576L; // 0x100000L
+    field public static final long ACTION_SET_RATING = 128L; // 0x80L
+    field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
+    field public static final long ACTION_SET_SHUFFLE_MODE = 2097152L; // 0x200000L
+    field @Deprecated public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
+    field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
+    field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
+    field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
+    field public static final long ACTION_STOP = 1L; // 0x1L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat!>! CREATOR;
+    field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
+    field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
+    field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
+    field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
+    field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
+    field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
+    field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
+    field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
+    field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
+    field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
+    field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
+    field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
+    field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
+    field public static final int REPEAT_MODE_ALL = 2; // 0x2
+    field public static final int REPEAT_MODE_GROUP = 3; // 0x3
+    field public static final int REPEAT_MODE_INVALID = -1; // 0xffffffff
+    field public static final int REPEAT_MODE_NONE = 0; // 0x0
+    field public static final int REPEAT_MODE_ONE = 1; // 0x1
+    field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
+    field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
+    field public static final int SHUFFLE_MODE_INVALID = -1; // 0xffffffff
+    field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
+    field public static final int STATE_BUFFERING = 6; // 0x6
+    field public static final int STATE_CONNECTING = 8; // 0x8
+    field public static final int STATE_ERROR = 7; // 0x7
+    field public static final int STATE_FAST_FORWARDING = 4; // 0x4
+    field public static final int STATE_NONE = 0; // 0x0
+    field public static final int STATE_PAUSED = 2; // 0x2
+    field public static final int STATE_PLAYING = 3; // 0x3
+    field public static final int STATE_REWINDING = 5; // 0x5
+    field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
+    field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
+    field public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; // 0xb
+    field public static final int STATE_STOPPED = 1; // 0x1
+  }
+
+  public static final class PlaybackStateCompat.Builder {
+    ctor public PlaybackStateCompat.Builder();
+    ctor public PlaybackStateCompat.Builder(android.support.v4.media.session.PlaybackStateCompat!);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! addCustomAction(String!, String!, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! addCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction!);
+    method public android.support.v4.media.session.PlaybackStateCompat! build();
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setActions(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setActiveQueueItemId(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setBufferedPosition(long);
+    method @Deprecated public android.support.v4.media.session.PlaybackStateCompat.Builder! setErrorMessage(CharSequence!);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setErrorMessage(int, CharSequence!);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setExtras(android.os.Bundle!);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setState(int, long, float);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setState(int, long, float, long);
+  }
+
+  public static final class PlaybackStateCompat.CustomAction implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat.CustomAction! fromCustomAction(Object!);
+    method public String! getAction();
+    method public Object! getCustomAction();
+    method public android.os.Bundle! getExtras();
+    method public int getIcon();
+    method public CharSequence! getName();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat.CustomAction!>! CREATOR;
+  }
+
+  public static final class PlaybackStateCompat.CustomAction.Builder {
+    ctor public PlaybackStateCompat.CustomAction.Builder(String!, CharSequence!, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction! build();
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder! setExtras(android.os.Bundle!);
+  }
+
+}
+
+package androidx.media {
+
+  public class AudioAttributesCompat implements androidx.versionedparcelable.VersionedParcelable {
+    method public int getContentType();
+    method public int getFlags();
+    method public int getLegacyStreamType();
+    method public int getUsage();
+    method public int getVolumeControlStream();
+    method public Object? unwrap();
+    method public static androidx.media.AudioAttributesCompat? wrap(Object);
+    field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
+    field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
+    field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
+    field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
+    field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
+    field public static final int FLAG_HW_AV_SYNC = 16; // 0x10
+    field public static final int USAGE_ALARM = 4; // 0x4
+    field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
+    field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
+    field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd
+    field public static final int USAGE_ASSISTANT = 16; // 0x10
+    field public static final int USAGE_GAME = 14; // 0xe
+    field public static final int USAGE_MEDIA = 1; // 0x1
+    field public static final int USAGE_NOTIFICATION = 5; // 0x5
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
+    field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
+    field public static final int USAGE_NOTIFICATION_RINGTONE = 6; // 0x6
+    field public static final int USAGE_UNKNOWN = 0; // 0x0
+    field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2
+    field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3
+  }
+
+  public static class AudioAttributesCompat.Builder {
+    ctor public AudioAttributesCompat.Builder();
+    ctor public AudioAttributesCompat.Builder(androidx.media.AudioAttributesCompat!);
+    method public androidx.media.AudioAttributesCompat! build();
+    method public androidx.media.AudioAttributesCompat.Builder! setContentType(int);
+    method public androidx.media.AudioAttributesCompat.Builder! setFlags(int);
+    method public androidx.media.AudioAttributesCompat.Builder! setLegacyStreamType(int);
+    method public androidx.media.AudioAttributesCompat.Builder! setUsage(int);
+  }
+
+  public class AudioFocusRequestCompat {
+    method public androidx.media.AudioAttributesCompat getAudioAttributesCompat();
+    method public android.os.Handler getFocusChangeHandler();
+    method public int getFocusGain();
+    method public android.media.AudioManager.OnAudioFocusChangeListener getOnAudioFocusChangeListener();
+    method public boolean willPauseWhenDucked();
+  }
+
+  public static final class AudioFocusRequestCompat.Builder {
+    ctor public AudioFocusRequestCompat.Builder(int);
+    ctor public AudioFocusRequestCompat.Builder(androidx.media.AudioFocusRequestCompat);
+    method public androidx.media.AudioFocusRequestCompat! build();
+    method public androidx.media.AudioFocusRequestCompat.Builder setAudioAttributes(androidx.media.AudioAttributesCompat);
+    method public androidx.media.AudioFocusRequestCompat.Builder setFocusGain(int);
+    method public androidx.media.AudioFocusRequestCompat.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener);
+    method public androidx.media.AudioFocusRequestCompat.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler);
+    method public androidx.media.AudioFocusRequestCompat.Builder setWillPauseWhenDucked(boolean);
+  }
+
+  public final class AudioManagerCompat {
+    method public static int abandonAudioFocusRequest(android.media.AudioManager, androidx.media.AudioFocusRequestCompat);
+    method public static int requestAudioFocus(android.media.AudioManager, androidx.media.AudioFocusRequestCompat);
+    field public static final int AUDIOFOCUS_GAIN = 1; // 0x1
+    field public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2; // 0x2
+    field public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = 4; // 0x4
+    field public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = 3; // 0x3
+  }
+
+  public abstract class MediaBrowserServiceCompat extends android.app.Service {
+    ctor public MediaBrowserServiceCompat();
+    method public void dump(java.io.FileDescriptor!, java.io.PrintWriter!, String![]!);
+    method public final android.os.Bundle! getBrowserRootHints();
+    method public final androidx.media.MediaSessionManager.RemoteUserInfo getCurrentBrowserInfo();
+    method public android.support.v4.media.session.MediaSessionCompat.Token? getSessionToken();
+    method public void notifyChildrenChanged(String);
+    method public void notifyChildrenChanged(String, android.os.Bundle);
+    method public android.os.IBinder! onBind(android.content.Intent!);
+    method public void onCustomAction(String, android.os.Bundle!, androidx.media.MediaBrowserServiceCompat.Result<android.os.Bundle!>);
+    method public abstract androidx.media.MediaBrowserServiceCompat.BrowserRoot? onGetRoot(String, int, android.os.Bundle?);
+    method public abstract void onLoadChildren(String, androidx.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>!>);
+    method public void onLoadChildren(String, androidx.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>!>, android.os.Bundle);
+    method public void onLoadItem(String!, androidx.media.MediaBrowserServiceCompat.Result<android.support.v4.media.MediaBrowserCompat.MediaItem!>);
+    method public void onSearch(String, android.os.Bundle!, androidx.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>!>);
+    method public void setSessionToken(android.support.v4.media.session.MediaSessionCompat.Token!);
+    field public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
+  }
+
+  public static final class MediaBrowserServiceCompat.BrowserRoot {
+    ctor public MediaBrowserServiceCompat.BrowserRoot(String, android.os.Bundle?);
+    method public android.os.Bundle! getExtras();
+    method public String! getRootId();
+    field public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+    field public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
+    field public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
+    field @Deprecated public static final String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
+  }
+
+  public static class MediaBrowserServiceCompat.Result<T> {
+    method public void detach();
+    method public void sendError(android.os.Bundle!);
+    method public void sendProgressUpdate(android.os.Bundle!);
+    method public void sendResult(T!);
+  }
+
+  public final class MediaSessionManager {
+    method public static androidx.media.MediaSessionManager getSessionManager(android.content.Context);
+    method public boolean isTrustedForMediaControl(androidx.media.MediaSessionManager.RemoteUserInfo);
+  }
+
+  public static final class MediaSessionManager.RemoteUserInfo {
+    ctor public MediaSessionManager.RemoteUserInfo(String, int, int);
+    method public String getPackageName();
+    method public int getPid();
+    method public int getUid();
+    field public static final String LEGACY_CONTROLLER = "android.media.session.MediaController";
+  }
+
+  public abstract class VolumeProviderCompat {
+    ctor public VolumeProviderCompat(int, int, int);
+    method public final int getCurrentVolume();
+    method public final int getMaxVolume();
+    method public final int getVolumeControl();
+    method public Object! getVolumeProvider();
+    method public void onAdjustVolume(int);
+    method public void onSetVolumeTo(int);
+    method public void setCallback(androidx.media.VolumeProviderCompat.Callback!);
+    method public final void setCurrentVolume(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
+  public abstract static class VolumeProviderCompat.Callback {
+    ctor public VolumeProviderCompat.Callback();
+    method public abstract void onVolumeChanged(androidx.media.VolumeProviderCompat!);
+  }
+
+}
+
+package androidx.media.app {
+
+  public class NotificationCompat {
+  }
+
+  public static class NotificationCompat.DecoratedMediaCustomViewStyle extends androidx.media.app.NotificationCompat.MediaStyle {
+    ctor public NotificationCompat.DecoratedMediaCustomViewStyle();
+  }
+
+  public static class NotificationCompat.MediaStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MediaStyle();
+    ctor public NotificationCompat.MediaStyle(androidx.core.app.NotificationCompat.Builder!);
+    method public static android.support.v4.media.session.MediaSessionCompat.Token! getMediaSession(android.app.Notification!);
+    method public androidx.media.app.NotificationCompat.MediaStyle! setCancelButtonIntent(android.app.PendingIntent!);
+    method public androidx.media.app.NotificationCompat.MediaStyle! setMediaSession(android.support.v4.media.session.MediaSessionCompat.Token!);
+    method public androidx.media.app.NotificationCompat.MediaStyle! setShowActionsInCompactView(int...);
+    method public androidx.media.app.NotificationCompat.MediaStyle! setShowCancelButton(boolean);
+  }
+
+}
+
+package androidx.media.session {
+
+  public class MediaButtonReceiver extends android.content.BroadcastReceiver {
+    ctor public MediaButtonReceiver();
+    method public static android.app.PendingIntent! buildMediaButtonPendingIntent(android.content.Context!, long);
+    method public static android.app.PendingIntent! buildMediaButtonPendingIntent(android.content.Context!, android.content.ComponentName!, long);
+    method public static android.view.KeyEvent! handleIntent(android.support.v4.media.session.MediaSessionCompat!, android.content.Intent!);
+    method public void onReceive(android.content.Context!, android.content.Intent!);
+  }
+
+}
+
diff --git a/media/api/restricted_1.2.0-alpha01.txt b/media/api/restricted_1.2.0-alpha01.txt
new file mode 100644
index 0000000..9c1f0db
--- /dev/null
+++ b/media/api/restricted_1.2.0-alpha01.txt
@@ -0,0 +1,743 @@
+// Signature format: 3.0
+package android.support.v4.media {
+
+  public final class MediaBrowserCompat {
+    ctor public MediaBrowserCompat(android.content.Context!, android.content.ComponentName!, android.support.v4.media.MediaBrowserCompat.ConnectionCallback!, android.os.Bundle!);
+    method public void connect();
+    method public void disconnect();
+    method public android.os.Bundle? getExtras();
+    method public void getItem(String, android.support.v4.media.MediaBrowserCompat.ItemCallback);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.os.Bundle? getNotifyChildrenChangedOptions();
+    method public String getRoot();
+    method public android.content.ComponentName getServiceComponent();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public boolean isConnected();
+    method public void search(String, android.os.Bundle!, android.support.v4.media.MediaBrowserCompat.SearchCallback);
+    method public void sendCustomAction(String, android.os.Bundle!, android.support.v4.media.MediaBrowserCompat.CustomActionCallback?);
+    method public void subscribe(String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void subscribe(String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void unsubscribe(String);
+    method public void unsubscribe(String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    field public static final String CUSTOM_ACTION_DOWNLOAD = "android.support.v4.media.action.DOWNLOAD";
+    field public static final String CUSTOM_ACTION_REMOVE_DOWNLOADED_FILE = "android.support.v4.media.action.REMOVE_DOWNLOADED_FILE";
+    field public static final String EXTRA_DOWNLOAD_PROGRESS = "android.media.browse.extra.DOWNLOAD_PROGRESS";
+    field public static final String EXTRA_MEDIA_ID = "android.media.browse.extra.MEDIA_ID";
+    field public static final String EXTRA_PAGE = "android.media.browse.extra.PAGE";
+    field public static final String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
+  }
+
+  public static class MediaBrowserCompat.ConnectionCallback {
+    ctor public MediaBrowserCompat.ConnectionCallback();
+    method public void onConnected();
+    method public void onConnectionFailed();
+    method public void onConnectionSuspended();
+  }
+
+  public abstract static class MediaBrowserCompat.CustomActionCallback {
+    ctor public MediaBrowserCompat.CustomActionCallback();
+    method public void onError(String!, android.os.Bundle!, android.os.Bundle!);
+    method public void onProgressUpdate(String!, android.os.Bundle!, android.os.Bundle!);
+    method public void onResult(String!, android.os.Bundle!, android.os.Bundle!);
+  }
+
+  public abstract static class MediaBrowserCompat.ItemCallback {
+    ctor public MediaBrowserCompat.ItemCallback();
+    method public void onError(String);
+    method public void onItemLoaded(android.support.v4.media.MediaBrowserCompat.MediaItem!);
+  }
+
+  public static class MediaBrowserCompat.MediaItem implements android.os.Parcelable {
+    ctor public MediaBrowserCompat.MediaItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaBrowserCompat.MediaItem! fromMediaItem(Object!);
+    method public static java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>! fromMediaItemList(java.util.List<?>!);
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public int getFlags();
+    method public String? getMediaId();
+    method public boolean isBrowsable();
+    method public boolean isPlayable();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaBrowserCompat.MediaItem!>! CREATOR;
+    field public static final int FLAG_BROWSABLE = 1; // 0x1
+    field public static final int FLAG_PLAYABLE = 2; // 0x2
+  }
+
+  public abstract static class MediaBrowserCompat.SearchCallback {
+    ctor public MediaBrowserCompat.SearchCallback();
+    method public void onError(String, android.os.Bundle!);
+    method public void onSearchResult(String, android.os.Bundle!, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>);
+  }
+
+  public abstract static class MediaBrowserCompat.SubscriptionCallback {
+    ctor public MediaBrowserCompat.SubscriptionCallback();
+    method public void onChildrenLoaded(String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>);
+    method public void onChildrenLoaded(String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>, android.os.Bundle);
+    method public void onError(String);
+    method public void onError(String, android.os.Bundle);
+  }
+
+  public final class MediaDescriptionCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.MediaDescriptionCompat! fromMediaDescription(Object!);
+    method public CharSequence? getDescription();
+    method public android.os.Bundle? getExtras();
+    method public android.graphics.Bitmap? getIconBitmap();
+    method public android.net.Uri? getIconUri();
+    method public Object! getMediaDescription();
+    method public String? getMediaId();
+    method public android.net.Uri? getMediaUri();
+    method public CharSequence? getSubtitle();
+    method public CharSequence? getTitle();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
+    field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
+    field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
+    field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
+    field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
+    field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
+    field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaDescriptionCompat!>! CREATOR;
+    field public static final String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE";
+    field public static final String EXTRA_DOWNLOAD_STATUS = "android.media.extra.DOWNLOAD_STATUS";
+    field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
+    field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
+    field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
+  }
+
+  public static final class MediaDescriptionCompat.Builder {
+    ctor public MediaDescriptionCompat.Builder();
+    method public android.support.v4.media.MediaDescriptionCompat! build();
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setDescription(CharSequence?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setExtras(android.os.Bundle?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setIconBitmap(android.graphics.Bitmap?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setIconUri(android.net.Uri?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setMediaId(String?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setMediaUri(android.net.Uri?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setSubtitle(CharSequence?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setTitle(CharSequence?);
+  }
+
+  public final class MediaMetadataCompat implements android.os.Parcelable {
+    method public boolean containsKey(String!);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaMetadataCompat! fromMediaMetadata(Object!);
+    method public android.graphics.Bitmap! getBitmap(String!);
+    method public android.os.Bundle! getBundle();
+    method public android.support.v4.media.MediaDescriptionCompat! getDescription();
+    method public long getLong(String!);
+    method public Object! getMediaMetadata();
+    method public android.support.v4.media.RatingCompat! getRating(String!);
+    method public String! getString(String!);
+    method public CharSequence! getText(String!);
+    method public java.util.Set<java.lang.String!>! keySet();
+    method public int size();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaMetadataCompat!>! CREATOR;
+    field public static final String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
+    field public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+    field public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+    field public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+    field public static final String METADATA_KEY_ART = "android.media.metadata.ART";
+    field public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+    field public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
+    field public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+    field public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
+    field public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+    field public static final String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+    field public static final String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+    field public static final String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+    field public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+    field public static final String METADATA_KEY_DOWNLOAD_STATUS = "android.media.metadata.DOWNLOAD_STATUS";
+    field public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+    field public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+    field public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+    field public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+    field public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
+    field public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+    field public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+    field public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public static final class MediaMetadataCompat.Builder {
+    ctor public MediaMetadataCompat.Builder();
+    ctor public MediaMetadataCompat.Builder(android.support.v4.media.MediaMetadataCompat!);
+    method public android.support.v4.media.MediaMetadataCompat! build();
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putBitmap(String!, android.graphics.Bitmap!);
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putLong(String!, long);
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putRating(String!, android.support.v4.media.RatingCompat!);
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putString(String!, String!);
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putText(String!, CharSequence!);
+  }
+
+  public final class RatingCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.RatingCompat! fromRating(Object!);
+    method public float getPercentRating();
+    method public Object! getRating();
+    method @android.support.v4.media.RatingCompat.Style public int getRatingStyle();
+    method public float getStarRating();
+    method public boolean hasHeart();
+    method public boolean isRated();
+    method public boolean isThumbUp();
+    method public static android.support.v4.media.RatingCompat! newHeartRating(boolean);
+    method public static android.support.v4.media.RatingCompat! newPercentageRating(float);
+    method public static android.support.v4.media.RatingCompat! newStarRating(@android.support.v4.media.RatingCompat.StarStyle int, float);
+    method public static android.support.v4.media.RatingCompat! newThumbRating(boolean);
+    method public static android.support.v4.media.RatingCompat! newUnratedRating(@android.support.v4.media.RatingCompat.Style int);
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.RatingCompat!>! CREATOR;
+    field public static final int RATING_3_STARS = 3; // 0x3
+    field public static final int RATING_4_STARS = 4; // 0x4
+    field public static final int RATING_5_STARS = 5; // 0x5
+    field public static final int RATING_HEART = 1; // 0x1
+    field public static final int RATING_NONE = 0; // 0x0
+    field public static final int RATING_PERCENTAGE = 6; // 0x6
+    field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
+  }
+
+
+  @IntDef({android.support.v4.media.RatingCompat.RATING_NONE, android.support.v4.media.RatingCompat.RATING_HEART, android.support.v4.media.RatingCompat.RATING_THUMB_UP_DOWN, android.support.v4.media.RatingCompat.RATING_3_STARS, android.support.v4.media.RatingCompat.RATING_4_STARS, android.support.v4.media.RatingCompat.RATING_5_STARS, android.support.v4.media.RatingCompat.RATING_PERCENTAGE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RatingCompat.Style {
+  }
+
+}
+
+package android.support.v4.media.session {
+
+  public final class MediaControllerCompat {
+    ctor public MediaControllerCompat(android.content.Context!, android.support.v4.media.session.MediaSessionCompat);
+    ctor public MediaControllerCompat(android.content.Context!, android.support.v4.media.session.MediaSessionCompat.Token);
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat!);
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat!, int);
+    method public void adjustVolume(int, int);
+    method public boolean dispatchMediaButtonEvent(android.view.KeyEvent!);
+    method public android.os.Bundle! getExtras();
+    method public long getFlags();
+    method public static android.support.v4.media.session.MediaControllerCompat! getMediaController(android.app.Activity);
+    method public Object! getMediaController();
+    method public android.support.v4.media.MediaMetadataCompat! getMetadata();
+    method public String! getPackageName();
+    method public android.support.v4.media.session.MediaControllerCompat.PlaybackInfo! getPlaybackInfo();
+    method public android.support.v4.media.session.PlaybackStateCompat! getPlaybackState();
+    method public java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem!>! getQueue();
+    method public CharSequence! getQueueTitle();
+    method public int getRatingType();
+    method public int getRepeatMode();
+    method public android.app.PendingIntent! getSessionActivity();
+    method public android.os.Bundle getSessionInfo();
+    method public android.support.v4.media.session.MediaSessionCompat.Token! getSessionToken();
+    method public int getShuffleMode();
+    method public android.support.v4.media.session.MediaControllerCompat.TransportControls! getTransportControls();
+    method public boolean isCaptioningEnabled();
+    method public boolean isSessionReady();
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback, android.os.Handler!);
+    method public void removeQueueItem(android.support.v4.media.MediaDescriptionCompat!);
+    method @Deprecated public void removeQueueItemAt(int);
+    method public void sendCommand(String, android.os.Bundle?, android.os.ResultReceiver?);
+    method public static void setMediaController(android.app.Activity, android.support.v4.media.session.MediaControllerCompat!);
+    method public void setVolumeTo(int, int);
+    method public void unregisterCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+  }
+
+  public abstract static class MediaControllerCompat.Callback implements android.os.IBinder.DeathRecipient {
+    ctor public MediaControllerCompat.Callback();
+    method public void binderDied();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.support.v4.media.session.IMediaControllerCallback! getIControllerCallback();
+    method public void onAudioInfoChanged(android.support.v4.media.session.MediaControllerCompat.PlaybackInfo!);
+    method public void onCaptioningEnabledChanged(boolean);
+    method public void onExtrasChanged(android.os.Bundle!);
+    method public void onMetadataChanged(android.support.v4.media.MediaMetadataCompat!);
+    method public void onPlaybackStateChanged(android.support.v4.media.session.PlaybackStateCompat!);
+    method public void onQueueChanged(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem!>!);
+    method public void onQueueTitleChanged(CharSequence!);
+    method public void onRepeatModeChanged(@android.support.v4.media.session.PlaybackStateCompat.RepeatMode int);
+    method public void onSessionDestroyed();
+    method public void onSessionEvent(String!, android.os.Bundle!);
+    method public void onSessionReady();
+    method public void onShuffleModeChanged(@android.support.v4.media.session.PlaybackStateCompat.ShuffleMode int);
+  }
+
+  public static final class MediaControllerCompat.PlaybackInfo {
+    method public androidx.media.AudioAttributesCompat getAudioAttributes();
+    method @Deprecated public int getAudioStream();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getPlaybackType();
+    method public int getVolumeControl();
+    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
+  }
+
+  public abstract static class MediaControllerCompat.TransportControls {
+    method public abstract void fastForward();
+    method public abstract void pause();
+    method public abstract void play();
+    method public abstract void playFromMediaId(String!, android.os.Bundle!);
+    method public abstract void playFromSearch(String!, android.os.Bundle!);
+    method public abstract void playFromUri(android.net.Uri!, android.os.Bundle!);
+    method public abstract void prepare();
+    method public abstract void prepareFromMediaId(String!, android.os.Bundle!);
+    method public abstract void prepareFromSearch(String!, android.os.Bundle!);
+    method public abstract void prepareFromUri(android.net.Uri!, android.os.Bundle!);
+    method public abstract void rewind();
+    method public abstract void seekTo(long);
+    method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction!, android.os.Bundle!);
+    method public abstract void sendCustomAction(String!, android.os.Bundle!);
+    method public abstract void setCaptioningEnabled(boolean);
+    method public void setPlaybackSpeed(float);
+    method public abstract void setRating(android.support.v4.media.RatingCompat!);
+    method public abstract void setRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
+    method public abstract void setRepeatMode(@android.support.v4.media.session.PlaybackStateCompat.RepeatMode int);
+    method public abstract void setShuffleMode(@android.support.v4.media.session.PlaybackStateCompat.ShuffleMode int);
+    method public abstract void skipToNext();
+    method public abstract void skipToPrevious();
+    method public abstract void skipToQueueItem(long);
+    method public abstract void stop();
+    field public static final String EXTRA_LEGACY_STREAM_TYPE = "android.media.session.extra.LEGACY_STREAM_TYPE";
+  }
+
+  public class MediaSessionCompat {
+    ctor public MediaSessionCompat(android.content.Context, String);
+    ctor public MediaSessionCompat(android.content.Context, String, android.content.ComponentName?, android.app.PendingIntent?);
+    ctor public MediaSessionCompat(android.content.Context, String, android.content.ComponentName?, android.app.PendingIntent?, android.os.Bundle?);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public MediaSessionCompat(android.content.Context, String, android.content.ComponentName?, android.app.PendingIntent?, android.os.Bundle?, androidx.versionedparcelable.VersionedParcelable?);
+    method public void addOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener!);
+    method public static android.support.v4.media.session.MediaSessionCompat! fromMediaSession(android.content.Context!, Object!);
+    method public android.support.v4.media.session.MediaControllerCompat! getController();
+    method public final androidx.media.MediaSessionManager.RemoteUserInfo getCurrentControllerInfo();
+    method public Object! getMediaSession();
+    method public Object! getRemoteControlClient();
+    method public android.support.v4.media.session.MediaSessionCompat.Token! getSessionToken();
+    method public boolean isActive();
+    method public void release();
+    method public void removeOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener!);
+    method public void sendSessionEvent(String!, android.os.Bundle!);
+    method public void setActive(boolean);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback!);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback!, android.os.Handler!);
+    method public void setCaptioningEnabled(boolean);
+    method public void setExtras(android.os.Bundle!);
+    method public void setFlags(int);
+    method public void setMediaButtonReceiver(android.app.PendingIntent!);
+    method public void setMetadata(android.support.v4.media.MediaMetadataCompat!);
+    method public void setPlaybackState(android.support.v4.media.session.PlaybackStateCompat!);
+    method public void setPlaybackToLocal(int);
+    method public void setPlaybackToRemote(androidx.media.VolumeProviderCompat!);
+    method public void setQueue(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem!>!);
+    method public void setQueueTitle(CharSequence!);
+    method public void setRatingType(@android.support.v4.media.RatingCompat.Style int);
+    method public void setRepeatMode(@android.support.v4.media.session.PlaybackStateCompat.RepeatMode int);
+    method public void setSessionActivity(android.app.PendingIntent!);
+    method public void setShuffleMode(@android.support.v4.media.session.PlaybackStateCompat.ShuffleMode int);
+    field public static final String ACTION_FLAG_AS_INAPPROPRIATE = "android.support.v4.media.session.action.FLAG_AS_INAPPROPRIATE";
+    field public static final String ACTION_FOLLOW = "android.support.v4.media.session.action.FOLLOW";
+    field public static final String ACTION_SKIP_AD = "android.support.v4.media.session.action.SKIP_AD";
+    field public static final String ACTION_UNFOLLOW = "android.support.v4.media.session.action.UNFOLLOW";
+    field public static final String ARGUMENT_MEDIA_ATTRIBUTE = "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE";
+    field public static final String ARGUMENT_MEDIA_ATTRIBUTE_VALUE = "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE_VALUE";
+    field @Deprecated public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
+    field @Deprecated public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+    field public static final int MEDIA_ATTRIBUTE_ALBUM = 1; // 0x1
+    field public static final int MEDIA_ATTRIBUTE_ARTIST = 0; // 0x0
+    field public static final int MEDIA_ATTRIBUTE_PLAYLIST = 2; // 0x2
+  }
+
+  public abstract static class MediaSessionCompat.Callback {
+    ctor public MediaSessionCompat.Callback();
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat!);
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat!, int);
+    method public void onCommand(String!, android.os.Bundle!, android.os.ResultReceiver!);
+    method public void onCustomAction(String!, android.os.Bundle!);
+    method public void onFastForward();
+    method public boolean onMediaButtonEvent(android.content.Intent!);
+    method public void onPause();
+    method public void onPlay();
+    method public void onPlayFromMediaId(String!, android.os.Bundle!);
+    method public void onPlayFromSearch(String!, android.os.Bundle!);
+    method public void onPlayFromUri(android.net.Uri!, android.os.Bundle!);
+    method public void onPrepare();
+    method public void onPrepareFromMediaId(String!, android.os.Bundle!);
+    method public void onPrepareFromSearch(String!, android.os.Bundle!);
+    method public void onPrepareFromUri(android.net.Uri!, android.os.Bundle!);
+    method public void onRemoveQueueItem(android.support.v4.media.MediaDescriptionCompat!);
+    method @Deprecated public void onRemoveQueueItemAt(int);
+    method public void onRewind();
+    method public void onSeekTo(long);
+    method public void onSetCaptioningEnabled(boolean);
+    method public void onSetPlaybackSpeed(float);
+    method public void onSetRating(android.support.v4.media.RatingCompat!);
+    method public void onSetRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
+    method public void onSetRepeatMode(@android.support.v4.media.session.PlaybackStateCompat.RepeatMode int);
+    method public void onSetShuffleMode(@android.support.v4.media.session.PlaybackStateCompat.ShuffleMode int);
+    method public void onSkipToNext();
+    method public void onSkipToPrevious();
+    method public void onSkipToQueueItem(long);
+    method public void onStop();
+  }
+
+  public static interface MediaSessionCompat.OnActiveChangeListener {
+    method public void onActiveChanged();
+  }
+
+  public static final class MediaSessionCompat.QueueItem implements android.os.Parcelable {
+    ctor public MediaSessionCompat.QueueItem(android.support.v4.media.MediaDescriptionCompat!, long);
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.QueueItem! fromQueueItem(Object!);
+    method public static java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem!>! fromQueueItemList(java.util.List<?>!);
+    method public android.support.v4.media.MediaDescriptionCompat! getDescription();
+    method public long getQueueId();
+    method public Object! getQueueItem();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.QueueItem!>! CREATOR;
+    field public static final int UNKNOWN_ID = -1; // 0xffffffff
+  }
+
+  public static final class MediaSessionCompat.Token implements android.os.Parcelable {
+    method public int describeContents();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.support.v4.media.session.MediaSessionCompat.Token! fromBundle(android.os.Bundle!);
+    method public static android.support.v4.media.session.MediaSessionCompat.Token! fromToken(Object!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.versionedparcelable.VersionedParcelable! getSession2Token();
+    method public Object! getToken();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSession2Token(androidx.versionedparcelable.VersionedParcelable!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.os.Bundle! toBundle();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.Token!>! CREATOR;
+  }
+
+  public class ParcelableVolumeInfo implements android.os.Parcelable {
+    ctor public ParcelableVolumeInfo(int, int, int, int, int);
+    ctor public ParcelableVolumeInfo(android.os.Parcel!);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.ParcelableVolumeInfo!>! CREATOR;
+    field public int audioStream;
+    field public int controlType;
+    field public int currentVolume;
+    field public int maxVolume;
+    field public int volumeType;
+  }
+
+  public final class PlaybackStateCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat! fromPlaybackState(Object!);
+    method @android.support.v4.media.session.PlaybackStateCompat.Actions public long getActions();
+    method public long getActiveQueueItemId();
+    method public long getBufferedPosition();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public long getCurrentPosition(Long!);
+    method public java.util.List<android.support.v4.media.session.PlaybackStateCompat.CustomAction!>! getCustomActions();
+    method public int getErrorCode();
+    method public CharSequence! getErrorMessage();
+    method public android.os.Bundle? getExtras();
+    method public long getLastPositionUpdateTime();
+    method public float getPlaybackSpeed();
+    method public Object! getPlaybackState();
+    method public long getPosition();
+    method @android.support.v4.media.session.PlaybackStateCompat.State public int getState();
+    method public static int toKeyCode(@android.support.v4.media.session.PlaybackStateCompat.MediaKeyAction long);
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
+    field public static final long ACTION_PAUSE = 2L; // 0x2L
+    field public static final long ACTION_PLAY = 4L; // 0x4L
+    field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
+    field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
+    field public static final long ACTION_PLAY_FROM_URI = 8192L; // 0x2000L
+    field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
+    field public static final long ACTION_PREPARE = 16384L; // 0x4000L
+    field public static final long ACTION_PREPARE_FROM_MEDIA_ID = 32768L; // 0x8000L
+    field public static final long ACTION_PREPARE_FROM_SEARCH = 65536L; // 0x10000L
+    field public static final long ACTION_PREPARE_FROM_URI = 131072L; // 0x20000L
+    field public static final long ACTION_REWIND = 8L; // 0x8L
+    field public static final long ACTION_SEEK_TO = 256L; // 0x100L
+    field public static final long ACTION_SET_CAPTIONING_ENABLED = 1048576L; // 0x100000L
+    field public static final long ACTION_SET_RATING = 128L; // 0x80L
+    field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
+    field public static final long ACTION_SET_SHUFFLE_MODE = 2097152L; // 0x200000L
+    field @Deprecated public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
+    field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
+    field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
+    field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
+    field public static final long ACTION_STOP = 1L; // 0x1L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat!>! CREATOR;
+    field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
+    field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
+    field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
+    field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
+    field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
+    field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
+    field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
+    field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
+    field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
+    field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
+    field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
+    field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
+    field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
+    field public static final int REPEAT_MODE_ALL = 2; // 0x2
+    field public static final int REPEAT_MODE_GROUP = 3; // 0x3
+    field public static final int REPEAT_MODE_INVALID = -1; // 0xffffffff
+    field public static final int REPEAT_MODE_NONE = 0; // 0x0
+    field public static final int REPEAT_MODE_ONE = 1; // 0x1
+    field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
+    field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
+    field public static final int SHUFFLE_MODE_INVALID = -1; // 0xffffffff
+    field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
+    field public static final int STATE_BUFFERING = 6; // 0x6
+    field public static final int STATE_CONNECTING = 8; // 0x8
+    field public static final int STATE_ERROR = 7; // 0x7
+    field public static final int STATE_FAST_FORWARDING = 4; // 0x4
+    field public static final int STATE_NONE = 0; // 0x0
+    field public static final int STATE_PAUSED = 2; // 0x2
+    field public static final int STATE_PLAYING = 3; // 0x3
+    field public static final int STATE_REWINDING = 5; // 0x5
+    field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
+    field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
+    field public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; // 0xb
+    field public static final int STATE_STOPPED = 1; // 0x1
+  }
+
+  @LongDef(flag=true, value={android.support.v4.media.session.PlaybackStateCompat.ACTION_STOP, android.support.v4.media.session.PlaybackStateCompat.ACTION_PAUSE, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY, android.support.v4.media.session.PlaybackStateCompat.ACTION_REWIND, android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS, android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_NEXT, android.support.v4.media.session.PlaybackStateCompat.ACTION_FAST_FORWARD, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_RATING, android.support.v4.media.session.PlaybackStateCompat.ACTION_SEEK_TO, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_PAUSE, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH, android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_URI, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE_FROM_URI, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_REPEAT_MODE, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_CAPTIONING_ENABLED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PlaybackStateCompat.Actions {
+  }
+
+  public static final class PlaybackStateCompat.Builder {
+    ctor public PlaybackStateCompat.Builder();
+    ctor public PlaybackStateCompat.Builder(android.support.v4.media.session.PlaybackStateCompat!);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! addCustomAction(String!, String!, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! addCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction!);
+    method public android.support.v4.media.session.PlaybackStateCompat! build();
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setActions(@android.support.v4.media.session.PlaybackStateCompat.Actions long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setActiveQueueItemId(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setBufferedPosition(long);
+    method @Deprecated public android.support.v4.media.session.PlaybackStateCompat.Builder! setErrorMessage(CharSequence!);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setErrorMessage(int, CharSequence!);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setExtras(android.os.Bundle!);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setState(@android.support.v4.media.session.PlaybackStateCompat.State int, long, float);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setState(@android.support.v4.media.session.PlaybackStateCompat.State int, long, float, long);
+  }
+
+  public static final class PlaybackStateCompat.CustomAction implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat.CustomAction! fromCustomAction(Object!);
+    method public String! getAction();
+    method public Object! getCustomAction();
+    method public android.os.Bundle! getExtras();
+    method public int getIcon();
+    method public CharSequence! getName();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat.CustomAction!>! CREATOR;
+  }
+
+  public static final class PlaybackStateCompat.CustomAction.Builder {
+    ctor public PlaybackStateCompat.CustomAction.Builder(String!, CharSequence!, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction! build();
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder! setExtras(android.os.Bundle!);
+  }
+
+
+  @IntDef({android.support.v4.media.session.PlaybackStateCompat.REPEAT_MODE_INVALID, android.support.v4.media.session.PlaybackStateCompat.REPEAT_MODE_NONE, android.support.v4.media.session.PlaybackStateCompat.REPEAT_MODE_ONE, android.support.v4.media.session.PlaybackStateCompat.REPEAT_MODE_ALL, android.support.v4.media.session.PlaybackStateCompat.REPEAT_MODE_GROUP}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PlaybackStateCompat.RepeatMode {
+  }
+
+  @IntDef({android.support.v4.media.session.PlaybackStateCompat.SHUFFLE_MODE_INVALID, android.support.v4.media.session.PlaybackStateCompat.SHUFFLE_MODE_NONE, android.support.v4.media.session.PlaybackStateCompat.SHUFFLE_MODE_ALL, android.support.v4.media.session.PlaybackStateCompat.SHUFFLE_MODE_GROUP}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PlaybackStateCompat.ShuffleMode {
+  }
+
+  @IntDef({android.support.v4.media.session.PlaybackStateCompat.STATE_NONE, android.support.v4.media.session.PlaybackStateCompat.STATE_STOPPED, android.support.v4.media.session.PlaybackStateCompat.STATE_PAUSED, android.support.v4.media.session.PlaybackStateCompat.STATE_PLAYING, android.support.v4.media.session.PlaybackStateCompat.STATE_FAST_FORWARDING, android.support.v4.media.session.PlaybackStateCompat.STATE_REWINDING, android.support.v4.media.session.PlaybackStateCompat.STATE_BUFFERING, android.support.v4.media.session.PlaybackStateCompat.STATE_ERROR, android.support.v4.media.session.PlaybackStateCompat.STATE_CONNECTING, android.support.v4.media.session.PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS, android.support.v4.media.session.PlaybackStateCompat.STATE_SKIPPING_TO_NEXT, android.support.v4.media.session.PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PlaybackStateCompat.State {
+  }
+
+}
+
+package androidx.media {
+
+  public class AudioAttributesCompat implements androidx.versionedparcelable.VersionedParcelable {
+    method public int getContentType();
+    method public int getFlags();
+    method public int getLegacyStreamType();
+    method @androidx.media.AudioAttributesCompat.AttributeUsage public int getUsage();
+    method public int getVolumeControlStream();
+    method public Object? unwrap();
+    method public static androidx.media.AudioAttributesCompat? wrap(Object);
+    field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
+    field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
+    field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
+    field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
+    field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
+    field public static final int FLAG_HW_AV_SYNC = 16; // 0x10
+    field public static final int USAGE_ALARM = 4; // 0x4
+    field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
+    field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
+    field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd
+    field public static final int USAGE_ASSISTANT = 16; // 0x10
+    field public static final int USAGE_GAME = 14; // 0xe
+    field public static final int USAGE_MEDIA = 1; // 0x1
+    field public static final int USAGE_NOTIFICATION = 5; // 0x5
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
+    field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
+    field public static final int USAGE_NOTIFICATION_RINGTONE = 6; // 0x6
+    field public static final int USAGE_UNKNOWN = 0; // 0x0
+    field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2
+    field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3
+  }
+
+
+
+  public static class AudioAttributesCompat.Builder {
+    ctor public AudioAttributesCompat.Builder();
+    ctor public AudioAttributesCompat.Builder(androidx.media.AudioAttributesCompat!);
+    method public androidx.media.AudioAttributesCompat! build();
+    method public androidx.media.AudioAttributesCompat.Builder! setContentType(@androidx.media.AudioAttributesCompat.AttributeContentType int);
+    method public androidx.media.AudioAttributesCompat.Builder! setFlags(int);
+    method public androidx.media.AudioAttributesCompat.Builder! setLegacyStreamType(int);
+    method public androidx.media.AudioAttributesCompat.Builder! setUsage(@androidx.media.AudioAttributesCompat.AttributeUsage int);
+  }
+
+
+
+
+  public class AudioFocusRequestCompat {
+    method public androidx.media.AudioAttributesCompat getAudioAttributesCompat();
+    method public android.os.Handler getFocusChangeHandler();
+    method public int getFocusGain();
+    method public android.media.AudioManager.OnAudioFocusChangeListener getOnAudioFocusChangeListener();
+    method public boolean willPauseWhenDucked();
+  }
+
+  public static final class AudioFocusRequestCompat.Builder {
+    ctor public AudioFocusRequestCompat.Builder(int);
+    ctor public AudioFocusRequestCompat.Builder(androidx.media.AudioFocusRequestCompat);
+    method public androidx.media.AudioFocusRequestCompat! build();
+    method public androidx.media.AudioFocusRequestCompat.Builder setAudioAttributes(androidx.media.AudioAttributesCompat);
+    method public androidx.media.AudioFocusRequestCompat.Builder setFocusGain(int);
+    method public androidx.media.AudioFocusRequestCompat.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener);
+    method public androidx.media.AudioFocusRequestCompat.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler);
+    method public androidx.media.AudioFocusRequestCompat.Builder setWillPauseWhenDucked(boolean);
+  }
+
+  public final class AudioManagerCompat {
+    method public static int abandonAudioFocusRequest(android.media.AudioManager, androidx.media.AudioFocusRequestCompat);
+    method public static int requestAudioFocus(android.media.AudioManager, androidx.media.AudioFocusRequestCompat);
+    field public static final int AUDIOFOCUS_GAIN = 1; // 0x1
+    field public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2; // 0x2
+    field public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = 4; // 0x4
+    field public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = 3; // 0x3
+  }
+
+
+
+  public abstract class MediaBrowserServiceCompat extends android.app.Service {
+    ctor public MediaBrowserServiceCompat();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void attachToBaseContext(android.content.Context!);
+    method public void dump(java.io.FileDescriptor!, java.io.PrintWriter!, String![]!);
+    method public final android.os.Bundle! getBrowserRootHints();
+    method public final androidx.media.MediaSessionManager.RemoteUserInfo getCurrentBrowserInfo();
+    method public android.support.v4.media.session.MediaSessionCompat.Token? getSessionToken();
+    method public void notifyChildrenChanged(String);
+    method public void notifyChildrenChanged(String, android.os.Bundle);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void notifyChildrenChanged(androidx.media.MediaSessionManager.RemoteUserInfo, String, android.os.Bundle);
+    method public android.os.IBinder! onBind(android.content.Intent!);
+    method public void onCustomAction(String, android.os.Bundle!, androidx.media.MediaBrowserServiceCompat.Result<android.os.Bundle!>);
+    method public abstract androidx.media.MediaBrowserServiceCompat.BrowserRoot? onGetRoot(String, int, android.os.Bundle?);
+    method public abstract void onLoadChildren(String, androidx.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>!>);
+    method public void onLoadChildren(String, androidx.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>!>, android.os.Bundle);
+    method public void onLoadItem(String!, androidx.media.MediaBrowserServiceCompat.Result<android.support.v4.media.MediaBrowserCompat.MediaItem!>);
+    method public void onSearch(String, android.os.Bundle!, androidx.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>!>);
+    method public void setSessionToken(android.support.v4.media.session.MediaSessionCompat.Token!);
+    field public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
+  }
+
+  public static final class MediaBrowserServiceCompat.BrowserRoot {
+    ctor public MediaBrowserServiceCompat.BrowserRoot(String, android.os.Bundle?);
+    method public android.os.Bundle! getExtras();
+    method public String! getRootId();
+    field public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+    field public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
+    field public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
+    field @Deprecated public static final String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
+  }
+
+  public static class MediaBrowserServiceCompat.Result<T> {
+    method public void detach();
+    method public void sendError(android.os.Bundle!);
+    method public void sendProgressUpdate(android.os.Bundle!);
+    method public void sendResult(T!);
+  }
+
+  public final class MediaSessionManager {
+    method public static androidx.media.MediaSessionManager getSessionManager(android.content.Context);
+    method public boolean isTrustedForMediaControl(androidx.media.MediaSessionManager.RemoteUserInfo);
+  }
+
+  public static final class MediaSessionManager.RemoteUserInfo {
+    ctor public MediaSessionManager.RemoteUserInfo(String, int, int);
+    method public String getPackageName();
+    method public int getPid();
+    method public int getUid();
+    field public static final String LEGACY_CONTROLLER = "android.media.session.MediaController";
+  }
+
+  public abstract class VolumeProviderCompat {
+    ctor public VolumeProviderCompat(@androidx.media.VolumeProviderCompat.ControlType int, int, int);
+    method public final int getCurrentVolume();
+    method public final int getMaxVolume();
+    method @androidx.media.VolumeProviderCompat.ControlType public final int getVolumeControl();
+    method public Object! getVolumeProvider();
+    method public void onAdjustVolume(int);
+    method public void onSetVolumeTo(int);
+    method public void setCallback(androidx.media.VolumeProviderCompat.Callback!);
+    method public final void setCurrentVolume(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
+  public abstract static class VolumeProviderCompat.Callback {
+    ctor public VolumeProviderCompat.Callback();
+    method public abstract void onVolumeChanged(androidx.media.VolumeProviderCompat!);
+  }
+
+  @IntDef({androidx.media.VolumeProviderCompat.VOLUME_CONTROL_FIXED, androidx.media.VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, androidx.media.VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface VolumeProviderCompat.ControlType {
+  }
+
+}
+
+package androidx.media.app {
+
+  public class NotificationCompat {
+  }
+
+  public static class NotificationCompat.DecoratedMediaCustomViewStyle extends androidx.media.app.NotificationCompat.MediaStyle {
+    ctor public NotificationCompat.DecoratedMediaCustomViewStyle();
+  }
+
+  public static class NotificationCompat.MediaStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MediaStyle();
+    ctor public NotificationCompat.MediaStyle(androidx.core.app.NotificationCompat.Builder!);
+    method public static android.support.v4.media.session.MediaSessionCompat.Token! getMediaSession(android.app.Notification!);
+    method public androidx.media.app.NotificationCompat.MediaStyle! setCancelButtonIntent(android.app.PendingIntent!);
+    method public androidx.media.app.NotificationCompat.MediaStyle! setMediaSession(android.support.v4.media.session.MediaSessionCompat.Token!);
+    method public androidx.media.app.NotificationCompat.MediaStyle! setShowActionsInCompactView(int...);
+    method public androidx.media.app.NotificationCompat.MediaStyle! setShowCancelButton(boolean);
+  }
+
+}
+
+package androidx.media.session {
+
+  public class MediaButtonReceiver extends android.content.BroadcastReceiver {
+    ctor public MediaButtonReceiver();
+    method public static android.app.PendingIntent! buildMediaButtonPendingIntent(android.content.Context!, @android.support.v4.media.session.PlaybackStateCompat.MediaKeyAction long);
+    method public static android.app.PendingIntent! buildMediaButtonPendingIntent(android.content.Context!, android.content.ComponentName!, @android.support.v4.media.session.PlaybackStateCompat.MediaKeyAction long);
+    method public static android.view.KeyEvent! handleIntent(android.support.v4.media.session.MediaSessionCompat!, android.content.Intent!);
+    method public void onReceive(android.content.Context!, android.content.Intent!);
+  }
+
+}
+
diff --git a/media/api/restricted_current.txt b/media/api/restricted_current.txt
new file mode 100644
index 0000000..9c1f0db
--- /dev/null
+++ b/media/api/restricted_current.txt
@@ -0,0 +1,743 @@
+// Signature format: 3.0
+package android.support.v4.media {
+
+  public final class MediaBrowserCompat {
+    ctor public MediaBrowserCompat(android.content.Context!, android.content.ComponentName!, android.support.v4.media.MediaBrowserCompat.ConnectionCallback!, android.os.Bundle!);
+    method public void connect();
+    method public void disconnect();
+    method public android.os.Bundle? getExtras();
+    method public void getItem(String, android.support.v4.media.MediaBrowserCompat.ItemCallback);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.os.Bundle? getNotifyChildrenChangedOptions();
+    method public String getRoot();
+    method public android.content.ComponentName getServiceComponent();
+    method public android.support.v4.media.session.MediaSessionCompat.Token getSessionToken();
+    method public boolean isConnected();
+    method public void search(String, android.os.Bundle!, android.support.v4.media.MediaBrowserCompat.SearchCallback);
+    method public void sendCustomAction(String, android.os.Bundle!, android.support.v4.media.MediaBrowserCompat.CustomActionCallback?);
+    method public void subscribe(String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void subscribe(String, android.os.Bundle, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    method public void unsubscribe(String);
+    method public void unsubscribe(String, android.support.v4.media.MediaBrowserCompat.SubscriptionCallback);
+    field public static final String CUSTOM_ACTION_DOWNLOAD = "android.support.v4.media.action.DOWNLOAD";
+    field public static final String CUSTOM_ACTION_REMOVE_DOWNLOADED_FILE = "android.support.v4.media.action.REMOVE_DOWNLOADED_FILE";
+    field public static final String EXTRA_DOWNLOAD_PROGRESS = "android.media.browse.extra.DOWNLOAD_PROGRESS";
+    field public static final String EXTRA_MEDIA_ID = "android.media.browse.extra.MEDIA_ID";
+    field public static final String EXTRA_PAGE = "android.media.browse.extra.PAGE";
+    field public static final String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
+  }
+
+  public static class MediaBrowserCompat.ConnectionCallback {
+    ctor public MediaBrowserCompat.ConnectionCallback();
+    method public void onConnected();
+    method public void onConnectionFailed();
+    method public void onConnectionSuspended();
+  }
+
+  public abstract static class MediaBrowserCompat.CustomActionCallback {
+    ctor public MediaBrowserCompat.CustomActionCallback();
+    method public void onError(String!, android.os.Bundle!, android.os.Bundle!);
+    method public void onProgressUpdate(String!, android.os.Bundle!, android.os.Bundle!);
+    method public void onResult(String!, android.os.Bundle!, android.os.Bundle!);
+  }
+
+  public abstract static class MediaBrowserCompat.ItemCallback {
+    ctor public MediaBrowserCompat.ItemCallback();
+    method public void onError(String);
+    method public void onItemLoaded(android.support.v4.media.MediaBrowserCompat.MediaItem!);
+  }
+
+  public static class MediaBrowserCompat.MediaItem implements android.os.Parcelable {
+    ctor public MediaBrowserCompat.MediaItem(android.support.v4.media.MediaDescriptionCompat, int);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaBrowserCompat.MediaItem! fromMediaItem(Object!);
+    method public static java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>! fromMediaItemList(java.util.List<?>!);
+    method public android.support.v4.media.MediaDescriptionCompat getDescription();
+    method public int getFlags();
+    method public String? getMediaId();
+    method public boolean isBrowsable();
+    method public boolean isPlayable();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaBrowserCompat.MediaItem!>! CREATOR;
+    field public static final int FLAG_BROWSABLE = 1; // 0x1
+    field public static final int FLAG_PLAYABLE = 2; // 0x2
+  }
+
+  public abstract static class MediaBrowserCompat.SearchCallback {
+    ctor public MediaBrowserCompat.SearchCallback();
+    method public void onError(String, android.os.Bundle!);
+    method public void onSearchResult(String, android.os.Bundle!, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>);
+  }
+
+  public abstract static class MediaBrowserCompat.SubscriptionCallback {
+    ctor public MediaBrowserCompat.SubscriptionCallback();
+    method public void onChildrenLoaded(String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>);
+    method public void onChildrenLoaded(String, java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>, android.os.Bundle);
+    method public void onError(String);
+    method public void onError(String, android.os.Bundle);
+  }
+
+  public final class MediaDescriptionCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.MediaDescriptionCompat! fromMediaDescription(Object!);
+    method public CharSequence? getDescription();
+    method public android.os.Bundle? getExtras();
+    method public android.graphics.Bitmap? getIconBitmap();
+    method public android.net.Uri? getIconUri();
+    method public Object! getMediaDescription();
+    method public String? getMediaId();
+    method public android.net.Uri? getMediaUri();
+    method public CharSequence? getSubtitle();
+    method public CharSequence? getTitle();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final long BT_FOLDER_TYPE_ALBUMS = 2L; // 0x2L
+    field public static final long BT_FOLDER_TYPE_ARTISTS = 3L; // 0x3L
+    field public static final long BT_FOLDER_TYPE_GENRES = 4L; // 0x4L
+    field public static final long BT_FOLDER_TYPE_MIXED = 0L; // 0x0L
+    field public static final long BT_FOLDER_TYPE_PLAYLISTS = 5L; // 0x5L
+    field public static final long BT_FOLDER_TYPE_TITLES = 1L; // 0x1L
+    field public static final long BT_FOLDER_TYPE_YEARS = 6L; // 0x6L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaDescriptionCompat!>! CREATOR;
+    field public static final String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE";
+    field public static final String EXTRA_DOWNLOAD_STATUS = "android.media.extra.DOWNLOAD_STATUS";
+    field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
+    field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
+    field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
+  }
+
+  public static final class MediaDescriptionCompat.Builder {
+    ctor public MediaDescriptionCompat.Builder();
+    method public android.support.v4.media.MediaDescriptionCompat! build();
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setDescription(CharSequence?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setExtras(android.os.Bundle?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setIconBitmap(android.graphics.Bitmap?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setIconUri(android.net.Uri?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setMediaId(String?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setMediaUri(android.net.Uri?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setSubtitle(CharSequence?);
+    method public android.support.v4.media.MediaDescriptionCompat.Builder! setTitle(CharSequence?);
+  }
+
+  public final class MediaMetadataCompat implements android.os.Parcelable {
+    method public boolean containsKey(String!);
+    method public int describeContents();
+    method public static android.support.v4.media.MediaMetadataCompat! fromMediaMetadata(Object!);
+    method public android.graphics.Bitmap! getBitmap(String!);
+    method public android.os.Bundle! getBundle();
+    method public android.support.v4.media.MediaDescriptionCompat! getDescription();
+    method public long getLong(String!);
+    method public Object! getMediaMetadata();
+    method public android.support.v4.media.RatingCompat! getRating(String!);
+    method public String! getString(String!);
+    method public CharSequence! getText(String!);
+    method public java.util.Set<java.lang.String!>! keySet();
+    method public int size();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.MediaMetadataCompat!>! CREATOR;
+    field public static final String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
+    field public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+    field public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+    field public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+    field public static final String METADATA_KEY_ART = "android.media.metadata.ART";
+    field public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+    field public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final String METADATA_KEY_BT_FOLDER_TYPE = "android.media.metadata.BT_FOLDER_TYPE";
+    field public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+    field public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
+    field public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+    field public static final String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+    field public static final String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+    field public static final String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+    field public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+    field public static final String METADATA_KEY_DOWNLOAD_STATUS = "android.media.metadata.DOWNLOAD_STATUS";
+    field public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+    field public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+    field public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+    field public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+    field public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
+    field public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+    field public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+    field public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+  }
+
+  public static final class MediaMetadataCompat.Builder {
+    ctor public MediaMetadataCompat.Builder();
+    ctor public MediaMetadataCompat.Builder(android.support.v4.media.MediaMetadataCompat!);
+    method public android.support.v4.media.MediaMetadataCompat! build();
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putBitmap(String!, android.graphics.Bitmap!);
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putLong(String!, long);
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putRating(String!, android.support.v4.media.RatingCompat!);
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putString(String!, String!);
+    method public android.support.v4.media.MediaMetadataCompat.Builder! putText(String!, CharSequence!);
+  }
+
+  public final class RatingCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.RatingCompat! fromRating(Object!);
+    method public float getPercentRating();
+    method public Object! getRating();
+    method @android.support.v4.media.RatingCompat.Style public int getRatingStyle();
+    method public float getStarRating();
+    method public boolean hasHeart();
+    method public boolean isRated();
+    method public boolean isThumbUp();
+    method public static android.support.v4.media.RatingCompat! newHeartRating(boolean);
+    method public static android.support.v4.media.RatingCompat! newPercentageRating(float);
+    method public static android.support.v4.media.RatingCompat! newStarRating(@android.support.v4.media.RatingCompat.StarStyle int, float);
+    method public static android.support.v4.media.RatingCompat! newThumbRating(boolean);
+    method public static android.support.v4.media.RatingCompat! newUnratedRating(@android.support.v4.media.RatingCompat.Style int);
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.RatingCompat!>! CREATOR;
+    field public static final int RATING_3_STARS = 3; // 0x3
+    field public static final int RATING_4_STARS = 4; // 0x4
+    field public static final int RATING_5_STARS = 5; // 0x5
+    field public static final int RATING_HEART = 1; // 0x1
+    field public static final int RATING_NONE = 0; // 0x0
+    field public static final int RATING_PERCENTAGE = 6; // 0x6
+    field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
+  }
+
+
+  @IntDef({android.support.v4.media.RatingCompat.RATING_NONE, android.support.v4.media.RatingCompat.RATING_HEART, android.support.v4.media.RatingCompat.RATING_THUMB_UP_DOWN, android.support.v4.media.RatingCompat.RATING_3_STARS, android.support.v4.media.RatingCompat.RATING_4_STARS, android.support.v4.media.RatingCompat.RATING_5_STARS, android.support.v4.media.RatingCompat.RATING_PERCENTAGE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RatingCompat.Style {
+  }
+
+}
+
+package android.support.v4.media.session {
+
+  public final class MediaControllerCompat {
+    ctor public MediaControllerCompat(android.content.Context!, android.support.v4.media.session.MediaSessionCompat);
+    ctor public MediaControllerCompat(android.content.Context!, android.support.v4.media.session.MediaSessionCompat.Token);
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat!);
+    method public void addQueueItem(android.support.v4.media.MediaDescriptionCompat!, int);
+    method public void adjustVolume(int, int);
+    method public boolean dispatchMediaButtonEvent(android.view.KeyEvent!);
+    method public android.os.Bundle! getExtras();
+    method public long getFlags();
+    method public static android.support.v4.media.session.MediaControllerCompat! getMediaController(android.app.Activity);
+    method public Object! getMediaController();
+    method public android.support.v4.media.MediaMetadataCompat! getMetadata();
+    method public String! getPackageName();
+    method public android.support.v4.media.session.MediaControllerCompat.PlaybackInfo! getPlaybackInfo();
+    method public android.support.v4.media.session.PlaybackStateCompat! getPlaybackState();
+    method public java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem!>! getQueue();
+    method public CharSequence! getQueueTitle();
+    method public int getRatingType();
+    method public int getRepeatMode();
+    method public android.app.PendingIntent! getSessionActivity();
+    method public android.os.Bundle getSessionInfo();
+    method public android.support.v4.media.session.MediaSessionCompat.Token! getSessionToken();
+    method public int getShuffleMode();
+    method public android.support.v4.media.session.MediaControllerCompat.TransportControls! getTransportControls();
+    method public boolean isCaptioningEnabled();
+    method public boolean isSessionReady();
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+    method public void registerCallback(android.support.v4.media.session.MediaControllerCompat.Callback, android.os.Handler!);
+    method public void removeQueueItem(android.support.v4.media.MediaDescriptionCompat!);
+    method @Deprecated public void removeQueueItemAt(int);
+    method public void sendCommand(String, android.os.Bundle?, android.os.ResultReceiver?);
+    method public static void setMediaController(android.app.Activity, android.support.v4.media.session.MediaControllerCompat!);
+    method public void setVolumeTo(int, int);
+    method public void unregisterCallback(android.support.v4.media.session.MediaControllerCompat.Callback);
+  }
+
+  public abstract static class MediaControllerCompat.Callback implements android.os.IBinder.DeathRecipient {
+    ctor public MediaControllerCompat.Callback();
+    method public void binderDied();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.support.v4.media.session.IMediaControllerCallback! getIControllerCallback();
+    method public void onAudioInfoChanged(android.support.v4.media.session.MediaControllerCompat.PlaybackInfo!);
+    method public void onCaptioningEnabledChanged(boolean);
+    method public void onExtrasChanged(android.os.Bundle!);
+    method public void onMetadataChanged(android.support.v4.media.MediaMetadataCompat!);
+    method public void onPlaybackStateChanged(android.support.v4.media.session.PlaybackStateCompat!);
+    method public void onQueueChanged(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem!>!);
+    method public void onQueueTitleChanged(CharSequence!);
+    method public void onRepeatModeChanged(@android.support.v4.media.session.PlaybackStateCompat.RepeatMode int);
+    method public void onSessionDestroyed();
+    method public void onSessionEvent(String!, android.os.Bundle!);
+    method public void onSessionReady();
+    method public void onShuffleModeChanged(@android.support.v4.media.session.PlaybackStateCompat.ShuffleMode int);
+  }
+
+  public static final class MediaControllerCompat.PlaybackInfo {
+    method public androidx.media.AudioAttributesCompat getAudioAttributes();
+    method @Deprecated public int getAudioStream();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getPlaybackType();
+    method public int getVolumeControl();
+    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
+  }
+
+  public abstract static class MediaControllerCompat.TransportControls {
+    method public abstract void fastForward();
+    method public abstract void pause();
+    method public abstract void play();
+    method public abstract void playFromMediaId(String!, android.os.Bundle!);
+    method public abstract void playFromSearch(String!, android.os.Bundle!);
+    method public abstract void playFromUri(android.net.Uri!, android.os.Bundle!);
+    method public abstract void prepare();
+    method public abstract void prepareFromMediaId(String!, android.os.Bundle!);
+    method public abstract void prepareFromSearch(String!, android.os.Bundle!);
+    method public abstract void prepareFromUri(android.net.Uri!, android.os.Bundle!);
+    method public abstract void rewind();
+    method public abstract void seekTo(long);
+    method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction!, android.os.Bundle!);
+    method public abstract void sendCustomAction(String!, android.os.Bundle!);
+    method public abstract void setCaptioningEnabled(boolean);
+    method public void setPlaybackSpeed(float);
+    method public abstract void setRating(android.support.v4.media.RatingCompat!);
+    method public abstract void setRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
+    method public abstract void setRepeatMode(@android.support.v4.media.session.PlaybackStateCompat.RepeatMode int);
+    method public abstract void setShuffleMode(@android.support.v4.media.session.PlaybackStateCompat.ShuffleMode int);
+    method public abstract void skipToNext();
+    method public abstract void skipToPrevious();
+    method public abstract void skipToQueueItem(long);
+    method public abstract void stop();
+    field public static final String EXTRA_LEGACY_STREAM_TYPE = "android.media.session.extra.LEGACY_STREAM_TYPE";
+  }
+
+  public class MediaSessionCompat {
+    ctor public MediaSessionCompat(android.content.Context, String);
+    ctor public MediaSessionCompat(android.content.Context, String, android.content.ComponentName?, android.app.PendingIntent?);
+    ctor public MediaSessionCompat(android.content.Context, String, android.content.ComponentName?, android.app.PendingIntent?, android.os.Bundle?);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public MediaSessionCompat(android.content.Context, String, android.content.ComponentName?, android.app.PendingIntent?, android.os.Bundle?, androidx.versionedparcelable.VersionedParcelable?);
+    method public void addOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener!);
+    method public static android.support.v4.media.session.MediaSessionCompat! fromMediaSession(android.content.Context!, Object!);
+    method public android.support.v4.media.session.MediaControllerCompat! getController();
+    method public final androidx.media.MediaSessionManager.RemoteUserInfo getCurrentControllerInfo();
+    method public Object! getMediaSession();
+    method public Object! getRemoteControlClient();
+    method public android.support.v4.media.session.MediaSessionCompat.Token! getSessionToken();
+    method public boolean isActive();
+    method public void release();
+    method public void removeOnActiveChangeListener(android.support.v4.media.session.MediaSessionCompat.OnActiveChangeListener!);
+    method public void sendSessionEvent(String!, android.os.Bundle!);
+    method public void setActive(boolean);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback!);
+    method public void setCallback(android.support.v4.media.session.MediaSessionCompat.Callback!, android.os.Handler!);
+    method public void setCaptioningEnabled(boolean);
+    method public void setExtras(android.os.Bundle!);
+    method public void setFlags(int);
+    method public void setMediaButtonReceiver(android.app.PendingIntent!);
+    method public void setMetadata(android.support.v4.media.MediaMetadataCompat!);
+    method public void setPlaybackState(android.support.v4.media.session.PlaybackStateCompat!);
+    method public void setPlaybackToLocal(int);
+    method public void setPlaybackToRemote(androidx.media.VolumeProviderCompat!);
+    method public void setQueue(java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem!>!);
+    method public void setQueueTitle(CharSequence!);
+    method public void setRatingType(@android.support.v4.media.RatingCompat.Style int);
+    method public void setRepeatMode(@android.support.v4.media.session.PlaybackStateCompat.RepeatMode int);
+    method public void setSessionActivity(android.app.PendingIntent!);
+    method public void setShuffleMode(@android.support.v4.media.session.PlaybackStateCompat.ShuffleMode int);
+    field public static final String ACTION_FLAG_AS_INAPPROPRIATE = "android.support.v4.media.session.action.FLAG_AS_INAPPROPRIATE";
+    field public static final String ACTION_FOLLOW = "android.support.v4.media.session.action.FOLLOW";
+    field public static final String ACTION_SKIP_AD = "android.support.v4.media.session.action.SKIP_AD";
+    field public static final String ACTION_UNFOLLOW = "android.support.v4.media.session.action.UNFOLLOW";
+    field public static final String ARGUMENT_MEDIA_ATTRIBUTE = "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE";
+    field public static final String ARGUMENT_MEDIA_ATTRIBUTE_VALUE = "android.support.v4.media.session.ARGUMENT_MEDIA_ATTRIBUTE_VALUE";
+    field @Deprecated public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
+    field @Deprecated public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+    field public static final int MEDIA_ATTRIBUTE_ALBUM = 1; // 0x1
+    field public static final int MEDIA_ATTRIBUTE_ARTIST = 0; // 0x0
+    field public static final int MEDIA_ATTRIBUTE_PLAYLIST = 2; // 0x2
+  }
+
+  public abstract static class MediaSessionCompat.Callback {
+    ctor public MediaSessionCompat.Callback();
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat!);
+    method public void onAddQueueItem(android.support.v4.media.MediaDescriptionCompat!, int);
+    method public void onCommand(String!, android.os.Bundle!, android.os.ResultReceiver!);
+    method public void onCustomAction(String!, android.os.Bundle!);
+    method public void onFastForward();
+    method public boolean onMediaButtonEvent(android.content.Intent!);
+    method public void onPause();
+    method public void onPlay();
+    method public void onPlayFromMediaId(String!, android.os.Bundle!);
+    method public void onPlayFromSearch(String!, android.os.Bundle!);
+    method public void onPlayFromUri(android.net.Uri!, android.os.Bundle!);
+    method public void onPrepare();
+    method public void onPrepareFromMediaId(String!, android.os.Bundle!);
+    method public void onPrepareFromSearch(String!, android.os.Bundle!);
+    method public void onPrepareFromUri(android.net.Uri!, android.os.Bundle!);
+    method public void onRemoveQueueItem(android.support.v4.media.MediaDescriptionCompat!);
+    method @Deprecated public void onRemoveQueueItemAt(int);
+    method public void onRewind();
+    method public void onSeekTo(long);
+    method public void onSetCaptioningEnabled(boolean);
+    method public void onSetPlaybackSpeed(float);
+    method public void onSetRating(android.support.v4.media.RatingCompat!);
+    method public void onSetRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
+    method public void onSetRepeatMode(@android.support.v4.media.session.PlaybackStateCompat.RepeatMode int);
+    method public void onSetShuffleMode(@android.support.v4.media.session.PlaybackStateCompat.ShuffleMode int);
+    method public void onSkipToNext();
+    method public void onSkipToPrevious();
+    method public void onSkipToQueueItem(long);
+    method public void onStop();
+  }
+
+  public static interface MediaSessionCompat.OnActiveChangeListener {
+    method public void onActiveChanged();
+  }
+
+  public static final class MediaSessionCompat.QueueItem implements android.os.Parcelable {
+    ctor public MediaSessionCompat.QueueItem(android.support.v4.media.MediaDescriptionCompat!, long);
+    method public int describeContents();
+    method public static android.support.v4.media.session.MediaSessionCompat.QueueItem! fromQueueItem(Object!);
+    method public static java.util.List<android.support.v4.media.session.MediaSessionCompat.QueueItem!>! fromQueueItemList(java.util.List<?>!);
+    method public android.support.v4.media.MediaDescriptionCompat! getDescription();
+    method public long getQueueId();
+    method public Object! getQueueItem();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.QueueItem!>! CREATOR;
+    field public static final int UNKNOWN_ID = -1; // 0xffffffff
+  }
+
+  public static final class MediaSessionCompat.Token implements android.os.Parcelable {
+    method public int describeContents();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.support.v4.media.session.MediaSessionCompat.Token! fromBundle(android.os.Bundle!);
+    method public static android.support.v4.media.session.MediaSessionCompat.Token! fromToken(Object!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.versionedparcelable.VersionedParcelable! getSession2Token();
+    method public Object! getToken();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSession2Token(androidx.versionedparcelable.VersionedParcelable!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.os.Bundle! toBundle();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.MediaSessionCompat.Token!>! CREATOR;
+  }
+
+  public class ParcelableVolumeInfo implements android.os.Parcelable {
+    ctor public ParcelableVolumeInfo(int, int, int, int, int);
+    ctor public ParcelableVolumeInfo(android.os.Parcel!);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.ParcelableVolumeInfo!>! CREATOR;
+    field public int audioStream;
+    field public int controlType;
+    field public int currentVolume;
+    field public int maxVolume;
+    field public int volumeType;
+  }
+
+  public final class PlaybackStateCompat implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat! fromPlaybackState(Object!);
+    method @android.support.v4.media.session.PlaybackStateCompat.Actions public long getActions();
+    method public long getActiveQueueItemId();
+    method public long getBufferedPosition();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public long getCurrentPosition(Long!);
+    method public java.util.List<android.support.v4.media.session.PlaybackStateCompat.CustomAction!>! getCustomActions();
+    method public int getErrorCode();
+    method public CharSequence! getErrorMessage();
+    method public android.os.Bundle? getExtras();
+    method public long getLastPositionUpdateTime();
+    method public float getPlaybackSpeed();
+    method public Object! getPlaybackState();
+    method public long getPosition();
+    method @android.support.v4.media.session.PlaybackStateCompat.State public int getState();
+    method public static int toKeyCode(@android.support.v4.media.session.PlaybackStateCompat.MediaKeyAction long);
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
+    field public static final long ACTION_PAUSE = 2L; // 0x2L
+    field public static final long ACTION_PLAY = 4L; // 0x4L
+    field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
+    field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
+    field public static final long ACTION_PLAY_FROM_URI = 8192L; // 0x2000L
+    field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
+    field public static final long ACTION_PREPARE = 16384L; // 0x4000L
+    field public static final long ACTION_PREPARE_FROM_MEDIA_ID = 32768L; // 0x8000L
+    field public static final long ACTION_PREPARE_FROM_SEARCH = 65536L; // 0x10000L
+    field public static final long ACTION_PREPARE_FROM_URI = 131072L; // 0x20000L
+    field public static final long ACTION_REWIND = 8L; // 0x8L
+    field public static final long ACTION_SEEK_TO = 256L; // 0x100L
+    field public static final long ACTION_SET_CAPTIONING_ENABLED = 1048576L; // 0x100000L
+    field public static final long ACTION_SET_RATING = 128L; // 0x80L
+    field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
+    field public static final long ACTION_SET_SHUFFLE_MODE = 2097152L; // 0x200000L
+    field @Deprecated public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
+    field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
+    field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
+    field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
+    field public static final long ACTION_STOP = 1L; // 0x1L
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat!>! CREATOR;
+    field public static final int ERROR_CODE_ACTION_ABORTED = 10; // 0xa
+    field public static final int ERROR_CODE_APP_ERROR = 1; // 0x1
+    field public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; // 0x3
+    field public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; // 0x5
+    field public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; // 0x8
+    field public static final int ERROR_CODE_END_OF_QUEUE = 11; // 0xb
+    field public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; // 0x7
+    field public static final int ERROR_CODE_NOT_SUPPORTED = 2; // 0x2
+    field public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; // 0x6
+    field public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; // 0x4
+    field public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; // 0x9
+    field public static final int ERROR_CODE_UNKNOWN_ERROR = 0; // 0x0
+    field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
+    field public static final int REPEAT_MODE_ALL = 2; // 0x2
+    field public static final int REPEAT_MODE_GROUP = 3; // 0x3
+    field public static final int REPEAT_MODE_INVALID = -1; // 0xffffffff
+    field public static final int REPEAT_MODE_NONE = 0; // 0x0
+    field public static final int REPEAT_MODE_ONE = 1; // 0x1
+    field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
+    field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
+    field public static final int SHUFFLE_MODE_INVALID = -1; // 0xffffffff
+    field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
+    field public static final int STATE_BUFFERING = 6; // 0x6
+    field public static final int STATE_CONNECTING = 8; // 0x8
+    field public static final int STATE_ERROR = 7; // 0x7
+    field public static final int STATE_FAST_FORWARDING = 4; // 0x4
+    field public static final int STATE_NONE = 0; // 0x0
+    field public static final int STATE_PAUSED = 2; // 0x2
+    field public static final int STATE_PLAYING = 3; // 0x3
+    field public static final int STATE_REWINDING = 5; // 0x5
+    field public static final int STATE_SKIPPING_TO_NEXT = 10; // 0xa
+    field public static final int STATE_SKIPPING_TO_PREVIOUS = 9; // 0x9
+    field public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; // 0xb
+    field public static final int STATE_STOPPED = 1; // 0x1
+  }
+
+  @LongDef(flag=true, value={android.support.v4.media.session.PlaybackStateCompat.ACTION_STOP, android.support.v4.media.session.PlaybackStateCompat.ACTION_PAUSE, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY, android.support.v4.media.session.PlaybackStateCompat.ACTION_REWIND, android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS, android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_NEXT, android.support.v4.media.session.PlaybackStateCompat.ACTION_FAST_FORWARD, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_RATING, android.support.v4.media.session.PlaybackStateCompat.ACTION_SEEK_TO, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_PAUSE, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH, android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM, android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_URI, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH, android.support.v4.media.session.PlaybackStateCompat.ACTION_PREPARE_FROM_URI, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_REPEAT_MODE, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE, android.support.v4.media.session.PlaybackStateCompat.ACTION_SET_CAPTIONING_ENABLED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PlaybackStateCompat.Actions {
+  }
+
+  public static final class PlaybackStateCompat.Builder {
+    ctor public PlaybackStateCompat.Builder();
+    ctor public PlaybackStateCompat.Builder(android.support.v4.media.session.PlaybackStateCompat!);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! addCustomAction(String!, String!, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! addCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction!);
+    method public android.support.v4.media.session.PlaybackStateCompat! build();
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setActions(@android.support.v4.media.session.PlaybackStateCompat.Actions long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setActiveQueueItemId(long);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setBufferedPosition(long);
+    method @Deprecated public android.support.v4.media.session.PlaybackStateCompat.Builder! setErrorMessage(CharSequence!);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setErrorMessage(int, CharSequence!);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setExtras(android.os.Bundle!);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setState(@android.support.v4.media.session.PlaybackStateCompat.State int, long, float);
+    method public android.support.v4.media.session.PlaybackStateCompat.Builder! setState(@android.support.v4.media.session.PlaybackStateCompat.State int, long, float, long);
+  }
+
+  public static final class PlaybackStateCompat.CustomAction implements android.os.Parcelable {
+    method public int describeContents();
+    method public static android.support.v4.media.session.PlaybackStateCompat.CustomAction! fromCustomAction(Object!);
+    method public String! getAction();
+    method public Object! getCustomAction();
+    method public android.os.Bundle! getExtras();
+    method public int getIcon();
+    method public CharSequence! getName();
+    method public void writeToParcel(android.os.Parcel!, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.media.session.PlaybackStateCompat.CustomAction!>! CREATOR;
+  }
+
+  public static final class PlaybackStateCompat.CustomAction.Builder {
+    ctor public PlaybackStateCompat.CustomAction.Builder(String!, CharSequence!, int);
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction! build();
+    method public android.support.v4.media.session.PlaybackStateCompat.CustomAction.Builder! setExtras(android.os.Bundle!);
+  }
+
+
+  @IntDef({android.support.v4.media.session.PlaybackStateCompat.REPEAT_MODE_INVALID, android.support.v4.media.session.PlaybackStateCompat.REPEAT_MODE_NONE, android.support.v4.media.session.PlaybackStateCompat.REPEAT_MODE_ONE, android.support.v4.media.session.PlaybackStateCompat.REPEAT_MODE_ALL, android.support.v4.media.session.PlaybackStateCompat.REPEAT_MODE_GROUP}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PlaybackStateCompat.RepeatMode {
+  }
+
+  @IntDef({android.support.v4.media.session.PlaybackStateCompat.SHUFFLE_MODE_INVALID, android.support.v4.media.session.PlaybackStateCompat.SHUFFLE_MODE_NONE, android.support.v4.media.session.PlaybackStateCompat.SHUFFLE_MODE_ALL, android.support.v4.media.session.PlaybackStateCompat.SHUFFLE_MODE_GROUP}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PlaybackStateCompat.ShuffleMode {
+  }
+
+  @IntDef({android.support.v4.media.session.PlaybackStateCompat.STATE_NONE, android.support.v4.media.session.PlaybackStateCompat.STATE_STOPPED, android.support.v4.media.session.PlaybackStateCompat.STATE_PAUSED, android.support.v4.media.session.PlaybackStateCompat.STATE_PLAYING, android.support.v4.media.session.PlaybackStateCompat.STATE_FAST_FORWARDING, android.support.v4.media.session.PlaybackStateCompat.STATE_REWINDING, android.support.v4.media.session.PlaybackStateCompat.STATE_BUFFERING, android.support.v4.media.session.PlaybackStateCompat.STATE_ERROR, android.support.v4.media.session.PlaybackStateCompat.STATE_CONNECTING, android.support.v4.media.session.PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS, android.support.v4.media.session.PlaybackStateCompat.STATE_SKIPPING_TO_NEXT, android.support.v4.media.session.PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PlaybackStateCompat.State {
+  }
+
+}
+
+package androidx.media {
+
+  public class AudioAttributesCompat implements androidx.versionedparcelable.VersionedParcelable {
+    method public int getContentType();
+    method public int getFlags();
+    method public int getLegacyStreamType();
+    method @androidx.media.AudioAttributesCompat.AttributeUsage public int getUsage();
+    method public int getVolumeControlStream();
+    method public Object? unwrap();
+    method public static androidx.media.AudioAttributesCompat? wrap(Object);
+    field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
+    field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
+    field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
+    field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
+    field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
+    field public static final int FLAG_HW_AV_SYNC = 16; // 0x10
+    field public static final int USAGE_ALARM = 4; // 0x4
+    field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
+    field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
+    field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd
+    field public static final int USAGE_ASSISTANT = 16; // 0x10
+    field public static final int USAGE_GAME = 14; // 0xe
+    field public static final int USAGE_MEDIA = 1; // 0x1
+    field public static final int USAGE_NOTIFICATION = 5; // 0x5
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
+    field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
+    field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
+    field public static final int USAGE_NOTIFICATION_RINGTONE = 6; // 0x6
+    field public static final int USAGE_UNKNOWN = 0; // 0x0
+    field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2
+    field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3
+  }
+
+
+
+  public static class AudioAttributesCompat.Builder {
+    ctor public AudioAttributesCompat.Builder();
+    ctor public AudioAttributesCompat.Builder(androidx.media.AudioAttributesCompat!);
+    method public androidx.media.AudioAttributesCompat! build();
+    method public androidx.media.AudioAttributesCompat.Builder! setContentType(@androidx.media.AudioAttributesCompat.AttributeContentType int);
+    method public androidx.media.AudioAttributesCompat.Builder! setFlags(int);
+    method public androidx.media.AudioAttributesCompat.Builder! setLegacyStreamType(int);
+    method public androidx.media.AudioAttributesCompat.Builder! setUsage(@androidx.media.AudioAttributesCompat.AttributeUsage int);
+  }
+
+
+
+
+  public class AudioFocusRequestCompat {
+    method public androidx.media.AudioAttributesCompat getAudioAttributesCompat();
+    method public android.os.Handler getFocusChangeHandler();
+    method public int getFocusGain();
+    method public android.media.AudioManager.OnAudioFocusChangeListener getOnAudioFocusChangeListener();
+    method public boolean willPauseWhenDucked();
+  }
+
+  public static final class AudioFocusRequestCompat.Builder {
+    ctor public AudioFocusRequestCompat.Builder(int);
+    ctor public AudioFocusRequestCompat.Builder(androidx.media.AudioFocusRequestCompat);
+    method public androidx.media.AudioFocusRequestCompat! build();
+    method public androidx.media.AudioFocusRequestCompat.Builder setAudioAttributes(androidx.media.AudioAttributesCompat);
+    method public androidx.media.AudioFocusRequestCompat.Builder setFocusGain(int);
+    method public androidx.media.AudioFocusRequestCompat.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener);
+    method public androidx.media.AudioFocusRequestCompat.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler);
+    method public androidx.media.AudioFocusRequestCompat.Builder setWillPauseWhenDucked(boolean);
+  }
+
+  public final class AudioManagerCompat {
+    method public static int abandonAudioFocusRequest(android.media.AudioManager, androidx.media.AudioFocusRequestCompat);
+    method public static int requestAudioFocus(android.media.AudioManager, androidx.media.AudioFocusRequestCompat);
+    field public static final int AUDIOFOCUS_GAIN = 1; // 0x1
+    field public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2; // 0x2
+    field public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = 4; // 0x4
+    field public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = 3; // 0x3
+  }
+
+
+
+  public abstract class MediaBrowserServiceCompat extends android.app.Service {
+    ctor public MediaBrowserServiceCompat();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void attachToBaseContext(android.content.Context!);
+    method public void dump(java.io.FileDescriptor!, java.io.PrintWriter!, String![]!);
+    method public final android.os.Bundle! getBrowserRootHints();
+    method public final androidx.media.MediaSessionManager.RemoteUserInfo getCurrentBrowserInfo();
+    method public android.support.v4.media.session.MediaSessionCompat.Token? getSessionToken();
+    method public void notifyChildrenChanged(String);
+    method public void notifyChildrenChanged(String, android.os.Bundle);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void notifyChildrenChanged(androidx.media.MediaSessionManager.RemoteUserInfo, String, android.os.Bundle);
+    method public android.os.IBinder! onBind(android.content.Intent!);
+    method public void onCustomAction(String, android.os.Bundle!, androidx.media.MediaBrowserServiceCompat.Result<android.os.Bundle!>);
+    method public abstract androidx.media.MediaBrowserServiceCompat.BrowserRoot? onGetRoot(String, int, android.os.Bundle?);
+    method public abstract void onLoadChildren(String, androidx.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>!>);
+    method public void onLoadChildren(String, androidx.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>!>, android.os.Bundle);
+    method public void onLoadItem(String!, androidx.media.MediaBrowserServiceCompat.Result<android.support.v4.media.MediaBrowserCompat.MediaItem!>);
+    method public void onSearch(String, android.os.Bundle!, androidx.media.MediaBrowserServiceCompat.Result<java.util.List<android.support.v4.media.MediaBrowserCompat.MediaItem!>!>);
+    method public void setSessionToken(android.support.v4.media.session.MediaSessionCompat.Token!);
+    field public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
+  }
+
+  public static final class MediaBrowserServiceCompat.BrowserRoot {
+    ctor public MediaBrowserServiceCompat.BrowserRoot(String, android.os.Bundle?);
+    method public android.os.Bundle! getExtras();
+    method public String! getRootId();
+    field public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+    field public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
+    field public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
+    field @Deprecated public static final String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
+  }
+
+  public static class MediaBrowserServiceCompat.Result<T> {
+    method public void detach();
+    method public void sendError(android.os.Bundle!);
+    method public void sendProgressUpdate(android.os.Bundle!);
+    method public void sendResult(T!);
+  }
+
+  public final class MediaSessionManager {
+    method public static androidx.media.MediaSessionManager getSessionManager(android.content.Context);
+    method public boolean isTrustedForMediaControl(androidx.media.MediaSessionManager.RemoteUserInfo);
+  }
+
+  public static final class MediaSessionManager.RemoteUserInfo {
+    ctor public MediaSessionManager.RemoteUserInfo(String, int, int);
+    method public String getPackageName();
+    method public int getPid();
+    method public int getUid();
+    field public static final String LEGACY_CONTROLLER = "android.media.session.MediaController";
+  }
+
+  public abstract class VolumeProviderCompat {
+    ctor public VolumeProviderCompat(@androidx.media.VolumeProviderCompat.ControlType int, int, int);
+    method public final int getCurrentVolume();
+    method public final int getMaxVolume();
+    method @androidx.media.VolumeProviderCompat.ControlType public final int getVolumeControl();
+    method public Object! getVolumeProvider();
+    method public void onAdjustVolume(int);
+    method public void onSetVolumeTo(int);
+    method public void setCallback(androidx.media.VolumeProviderCompat.Callback!);
+    method public final void setCurrentVolume(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
+  public abstract static class VolumeProviderCompat.Callback {
+    ctor public VolumeProviderCompat.Callback();
+    method public abstract void onVolumeChanged(androidx.media.VolumeProviderCompat!);
+  }
+
+  @IntDef({androidx.media.VolumeProviderCompat.VOLUME_CONTROL_FIXED, androidx.media.VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, androidx.media.VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface VolumeProviderCompat.ControlType {
+  }
+
+}
+
+package androidx.media.app {
+
+  public class NotificationCompat {
+  }
+
+  public static class NotificationCompat.DecoratedMediaCustomViewStyle extends androidx.media.app.NotificationCompat.MediaStyle {
+    ctor public NotificationCompat.DecoratedMediaCustomViewStyle();
+  }
+
+  public static class NotificationCompat.MediaStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MediaStyle();
+    ctor public NotificationCompat.MediaStyle(androidx.core.app.NotificationCompat.Builder!);
+    method public static android.support.v4.media.session.MediaSessionCompat.Token! getMediaSession(android.app.Notification!);
+    method public androidx.media.app.NotificationCompat.MediaStyle! setCancelButtonIntent(android.app.PendingIntent!);
+    method public androidx.media.app.NotificationCompat.MediaStyle! setMediaSession(android.support.v4.media.session.MediaSessionCompat.Token!);
+    method public androidx.media.app.NotificationCompat.MediaStyle! setShowActionsInCompactView(int...);
+    method public androidx.media.app.NotificationCompat.MediaStyle! setShowCancelButton(boolean);
+  }
+
+}
+
+package androidx.media.session {
+
+  public class MediaButtonReceiver extends android.content.BroadcastReceiver {
+    ctor public MediaButtonReceiver();
+    method public static android.app.PendingIntent! buildMediaButtonPendingIntent(android.content.Context!, @android.support.v4.media.session.PlaybackStateCompat.MediaKeyAction long);
+    method public static android.app.PendingIntent! buildMediaButtonPendingIntent(android.content.Context!, android.content.ComponentName!, @android.support.v4.media.session.PlaybackStateCompat.MediaKeyAction long);
+    method public static android.view.KeyEvent! handleIntent(android.support.v4.media.session.MediaSessionCompat!, android.content.Intent!);
+    method public void onReceive(android.content.Context!, android.content.Intent!);
+  }
+
+}
+
diff --git a/media/build.gradle b/media/build.gradle
index 863a93c..facba79 100644
--- a/media/build.gradle
+++ b/media/build.gradle
@@ -40,5 +40,4 @@
     inceptionYear = "2011"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later."
     failOnDeprecationWarnings = false
-    trackRestrictedAPIs = false // TODO: Remove it (b/131031933)
 }
diff --git a/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java b/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java
index eddc7a8..4ec5200 100644
--- a/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java
+++ b/media/src/main/java/android/support/v4/media/MediaBrowserCompat.java
@@ -446,7 +446,7 @@
      *         String, Bundle)}
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY_GROUP_PREFIX) // accessed by media2-session
     @Nullable
     public Bundle getNotifyChildrenChangedOptions() {
         return mImpl.getNotifyChildrenChangedOptions();
@@ -462,11 +462,9 @@
         private final int mFlags;
         private final MediaDescriptionCompat mDescription;
 
-        /** @hide */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
         @Retention(RetentionPolicy.SOURCE)
         @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
-        public @interface Flags { }
+        private @interface Flags { }
 
         /**
          * Flag: Indicates that the item has children of its own.
diff --git a/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java b/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
index e34708f..a4734db 100644
--- a/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
+++ b/media/src/main/java/android/support/v4/media/MediaDescriptionCompat.java
@@ -15,7 +15,7 @@
  */
 package android.support.v4.media;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.annotation.SuppressLint;
 import android.graphics.Bitmap;
@@ -140,7 +140,7 @@
      *
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     public static final String DESCRIPTION_KEY_MEDIA_URI =
             "android.support.v4.media.description.MEDIA_URI";
     /**
@@ -148,7 +148,7 @@
      *
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     public static final String DESCRIPTION_KEY_NULL_BUNDLE_FLAG =
             "android.support.v4.media.description.NULL_BUNDLE_FLAG";
     /**
diff --git a/media/src/main/java/android/support/v4/media/MediaMetadataCompat.java b/media/src/main/java/android/support/v4/media/MediaMetadataCompat.java
index bf8b780..96c192c 100644
--- a/media/src/main/java/android/support/v4/media/MediaMetadataCompat.java
+++ b/media/src/main/java/android/support/v4/media/MediaMetadataCompat.java
@@ -15,7 +15,7 @@
  */
 package android.support.v4.media;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.annotation.SuppressLint;
 import android.graphics.Bitmap;
@@ -262,10 +262,6 @@
     public static final String METADATA_KEY_DOWNLOAD_STATUS =
             "android.media.metadata.DOWNLOAD_STATUS";
 
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
     @StringDef(value =
             {METADATA_KEY_TITLE, METADATA_KEY_ARTIST, METADATA_KEY_ALBUM, METADATA_KEY_AUTHOR,
             METADATA_KEY_WRITER, METADATA_KEY_COMPOSER, METADATA_KEY_COMPILATION,
@@ -275,35 +271,23 @@
             METADATA_KEY_MEDIA_ID, METADATA_KEY_MEDIA_URI},
             open = true)
     @Retention(RetentionPolicy.SOURCE)
-    public @interface TextKey {}
+    private @interface TextKey {}
 
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
     @StringDef(value =
             {METADATA_KEY_DURATION, METADATA_KEY_YEAR, METADATA_KEY_TRACK_NUMBER,
             METADATA_KEY_NUM_TRACKS, METADATA_KEY_DISC_NUMBER, METADATA_KEY_BT_FOLDER_TYPE,
             METADATA_KEY_ADVERTISEMENT, METADATA_KEY_DOWNLOAD_STATUS},
             open = true)
     @Retention(RetentionPolicy.SOURCE)
-    public @interface LongKey {}
+    private @interface LongKey {}
 
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
     @StringDef({METADATA_KEY_ART, METADATA_KEY_ALBUM_ART, METADATA_KEY_DISPLAY_ICON})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface BitmapKey {}
+    private @interface BitmapKey {}
 
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
     @StringDef({METADATA_KEY_USER_RATING, METADATA_KEY_RATING})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface RatingKey {}
+    private @interface RatingKey {}
 
     static final int METADATA_TYPE_LONG = 0;
     static final int METADATA_TYPE_TEXT = 1;
@@ -695,7 +679,7 @@
          *            in the metadata.
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         public Builder(MediaMetadataCompat source, int maxBitmapSize) {
             this(source);
             for (String key : mBundle.keySet()) {
diff --git a/media/src/main/java/android/support/v4/media/RatingCompat.java b/media/src/main/java/android/support/v4/media/RatingCompat.java
index d109647..42b6da0 100644
--- a/media/src/main/java/android/support/v4/media/RatingCompat.java
+++ b/media/src/main/java/android/support/v4/media/RatingCompat.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.media;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
 
 import android.annotation.SuppressLint;
@@ -46,7 +47,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY_GROUP_PREFIX) // used by media2-session
     @IntDef({RATING_NONE, RATING_HEART, RATING_THUMB_UP_DOWN, RATING_3_STARS, RATING_4_STARS,
             RATING_5_STARS, RATING_PERCENTAGE})
     @Retention(RetentionPolicy.SOURCE)
@@ -55,7 +56,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     @IntDef({RATING_3_STARS, RATING_4_STARS, RATING_5_STARS})
     @Retention(RetentionPolicy.SOURCE)
     public @interface StarStyle {}
diff --git a/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java b/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
index a6bf8dd..7ed8a4a 100644
--- a/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -514,7 +514,7 @@
      * @return The session's token as VersionedParcelable.
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     @Nullable
     public VersionedParcelable getSession2Token() {
         return mToken.getSession2Token();
@@ -815,7 +815,7 @@
         /**
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY_GROUP_PREFIX) // accessed by media2-session
         public IMediaControllerCallback getIControllerCallback() {
             return mIControllerCallback;
         }
diff --git a/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java b/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
index e54467b9..bbf6980 100644
--- a/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -125,16 +125,12 @@
     private final MediaControllerCompat mController;
     private final ArrayList<OnActiveChangeListener> mActiveListeners = new ArrayList<>();
 
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
     @IntDef(flag=true, value={
             FLAG_HANDLES_MEDIA_BUTTONS,
             FLAG_HANDLES_TRANSPORT_CONTROLS,
             FLAG_HANDLES_QUEUE_COMMANDS })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface SessionFlags {}
+    private @interface SessionFlags {}
 
     /**
      * Sets this flag on the session to indicate that it can handle media button
@@ -422,7 +418,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     public static final String KEY_TOKEN = "android.support.v4.media.session.TOKEN";
 
     /**
@@ -435,7 +431,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     public static final String KEY_SESSION2_TOKEN =
             "android.support.v4.media.session.SESSION_TOKEN2";
 
@@ -533,7 +529,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY_GROUP_PREFIX) // accessed by media2-session
     public MediaSessionCompat(@NonNull Context context, @NonNull String tag,
             @Nullable ComponentName mbrComponent, @Nullable PendingIntent mbrIntent,
             @Nullable Bundle sessionInfo, @Nullable VersionedParcelable session2Token) {
@@ -964,7 +960,7 @@
      *
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     public String getCallingPackage() {
         return mImpl.getCallingPackage();
     }
@@ -1031,11 +1027,8 @@
 
     /**
      * Returns whether the given bundle includes non-framework Parcelables.
-     *
-     * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
-    public static boolean doesBundleHaveCustomParcelable(@Nullable Bundle bundle) {
+    static boolean doesBundleHaveCustomParcelable(@Nullable Bundle bundle) {
         if (bundle == null) {
             return false;
         }
@@ -1066,7 +1059,7 @@
      *
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     public static void ensureClassLoader(@Nullable Bundle bundle) {
         if (bundle != null) {
             bundle.setClassLoader(MediaSessionCompat.class.getClassLoader());
@@ -1832,7 +1825,7 @@
          * @return A compat Token for use with {@link MediaControllerCompat}.
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         public static Token fromToken(Object token, IMediaSession extraBinder) {
             if (token != null && android.os.Build.VERSION.SDK_INT >= 21) {
                 if (!(token instanceof MediaSession.Token)) {
@@ -1901,7 +1894,7 @@
         /**
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         public IMediaSession getExtraBinder() {
             return mExtraBinder;
         }
@@ -1909,7 +1902,7 @@
         /**
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         public void setExtraBinder(IMediaSession extraBinder) {
             mExtraBinder = extraBinder;
         }
@@ -1917,7 +1910,7 @@
         /**
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY_GROUP_PREFIX) // accessed by media2-session
         public VersionedParcelable getSession2Token() {
             return mSession2Token;
         }
@@ -1925,7 +1918,7 @@
         /**
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY_GROUP_PREFIX) // accessed by media2-session
         public void setSession2Token(VersionedParcelable session2Token) {
             mSession2Token = session2Token;
         }
@@ -1933,7 +1926,7 @@
         /**
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY_GROUP_PREFIX) // accessed by media2-session
         public Bundle toBundle() {
             Bundle bundle = new Bundle();
             bundle.putParcelable(KEY_TOKEN, this);
@@ -1953,7 +1946,7 @@
          * @return A compat Token for use with {@link MediaControllerCompat}.
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY_GROUP_PREFIX) // accessed by media2-session
         public static Token fromBundle(Bundle tokenBundle) {
             if (tokenBundle == null) {
                 return null;
@@ -2147,12 +2140,9 @@
      * This is a wrapper for {@link ResultReceiver} for sending over aidl
      * interfaces. The framework version was not exposed to aidls until
      * {@link android.os.Build.VERSION_CODES#LOLLIPOP}.
-     *
-     * @hide
      */
-    @RestrictTo(LIBRARY)
     @SuppressLint("BanParcelableUsage")
-    public static final class ResultReceiverWrapper implements Parcelable {
+    /* package */ static final class ResultReceiverWrapper implements Parcelable {
         ResultReceiver mResultReceiver;
 
         public ResultReceiverWrapper(@NonNull ResultReceiver resultReceiver) {
diff --git a/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java b/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
index d1399d6..164a459 100644
--- a/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
+++ b/media/src/main/java/android/support/v4/media/session/PlaybackStateCompat.java
@@ -15,6 +15,7 @@
  */
 package android.support.v4.media.session;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
 
 import android.annotation.SuppressLint;
@@ -48,7 +49,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY_GROUP_PREFIX) // used by media2-session
     @LongDef(flag=true, value={ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND,
             ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING,
             ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH,
@@ -61,7 +62,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     @LongDef({ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND, ACTION_SKIP_TO_PREVIOUS,
             ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_PLAY_PAUSE})
     @Retention(RetentionPolicy.SOURCE)
@@ -226,7 +227,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY_GROUP_PREFIX) // used by media2-session
     @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING,
             STATE_REWINDING, STATE_BUFFERING, STATE_ERROR, STATE_CONNECTING,
             STATE_SKIPPING_TO_PREVIOUS, STATE_SKIPPING_TO_NEXT, STATE_SKIPPING_TO_QUEUE_ITEM})
@@ -339,7 +340,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY_GROUP_PREFIX) // used by media2-session
     @IntDef({REPEAT_MODE_INVALID, REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL,
             REPEAT_MODE_GROUP})
     @Retention(RetentionPolicy.SOURCE)
@@ -380,7 +381,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY_GROUP_PREFIX) // used by media2-session
     @IntDef({SHUFFLE_MODE_INVALID, SHUFFLE_MODE_NONE, SHUFFLE_MODE_ALL, SHUFFLE_MODE_GROUP})
     @Retention(RetentionPolicy.SOURCE)
     public @interface ShuffleMode {}
@@ -411,17 +412,13 @@
      */
     public static final int SHUFFLE_MODE_GROUP = 2;
 
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
     @IntDef({ERROR_CODE_UNKNOWN_ERROR, ERROR_CODE_APP_ERROR, ERROR_CODE_NOT_SUPPORTED,
             ERROR_CODE_AUTHENTICATION_EXPIRED, ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED,
             ERROR_CODE_CONCURRENT_STREAM_LIMIT, ERROR_CODE_PARENTAL_CONTROL_RESTRICTED,
             ERROR_CODE_NOT_AVAILABLE_IN_REGION, ERROR_CODE_CONTENT_ALREADY_PLAYING,
             ERROR_CODE_SKIP_LIMIT_REACHED, ERROR_CODE_ACTION_ABORTED, ERROR_CODE_END_OF_QUEUE})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface ErrorCode {}
+    private @interface ErrorCode {}
 
     /**
      * This is the default error code and indicates that none of the other error codes applies.
@@ -668,7 +665,7 @@
      * @return The current playback position in ms
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY_GROUP_PREFIX) // accessed by media2-session
     public long getCurrentPosition(Long timeDiff) {
         long expectedPosition = mPosition + (long) (mSpeed * (
                 (timeDiff != null) ? timeDiff : SystemClock.elapsedRealtime() - mUpdateTime));
diff --git a/media/src/main/java/androidx/media/AudioAttributesCompat.java b/media/src/main/java/androidx/media/AudioAttributesCompat.java
index 730daff..7337f93 100644
--- a/media/src/main/java/androidx/media/AudioAttributesCompat.java
+++ b/media/src/main/java/androidx/media/AudioAttributesCompat.java
@@ -16,7 +16,7 @@
 
 package androidx.media;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.media.AudioAttributes;
 import android.media.AudioManager;
@@ -234,12 +234,8 @@
 
     static final int INVALID_STREAM_TYPE = -1;  // AudioSystem.STREAM_DEFAULT
 
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
     @ParcelField(1)
-    public AudioAttributesImpl mImpl;
+    AudioAttributesImpl mImpl;
 
     AudioAttributesCompat() {
     }
@@ -538,7 +534,7 @@
      *
      * @hide For testing only.
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     public static void setForceLegacyBehavior(boolean force) {
         sForceLegacyBehavior = force;
     }
@@ -631,7 +627,7 @@
             USAGE_GAME,
             USAGE_ASSISTANT,
     })
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     @Retention(RetentionPolicy.SOURCE)
     public @interface AttributeUsage {
     }
@@ -645,7 +641,7 @@
             CONTENT_TYPE_SONIFICATION
     })
     @Retention(RetentionPolicy.SOURCE)
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     public @interface AttributeContentType {
     }
 }
diff --git a/media/src/main/java/androidx/media/AudioAttributesImplApi21.java b/media/src/main/java/androidx/media/AudioAttributesImplApi21.java
index 81b9c2c..bddc8a6 100644
--- a/media/src/main/java/androidx/media/AudioAttributesImplApi21.java
+++ b/media/src/main/java/androidx/media/AudioAttributesImplApi21.java
@@ -16,7 +16,7 @@
 
 package androidx.media;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.media.AudioAttributesCompat.INVALID_STREAM_TYPE;
 
 import android.annotation.SuppressLint;
@@ -29,20 +29,16 @@
 
 /** @hide */
 @VersionedParcelize(jetifyAs = "android.support.v4.media.AudioAttributesImplApi21")
-@RestrictTo(LIBRARY_GROUP_PREFIX)
+@RestrictTo(LIBRARY)
 @RequiresApi(21)
 public class AudioAttributesImplApi21 implements AudioAttributesImpl {
     private static final String TAG = "AudioAttributesCompat21";
 
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
     @ParcelField(1)
-    public AudioAttributes mAudioAttributes;
+    AudioAttributes mAudioAttributes;
 
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
     @ParcelField(2)
-    public int mLegacyStreamType = INVALID_STREAM_TYPE;
+    int mLegacyStreamType = INVALID_STREAM_TYPE;
 
     /**
      * Used for VersionedParcelable
diff --git a/media/src/main/java/androidx/media/AudioAttributesImplApi26.java b/media/src/main/java/androidx/media/AudioAttributesImplApi26.java
index 12e14cb..21411ff 100644
--- a/media/src/main/java/androidx/media/AudioAttributesImplApi26.java
+++ b/media/src/main/java/androidx/media/AudioAttributesImplApi26.java
@@ -16,7 +16,7 @@
 
 package androidx.media;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.media.AudioAttributesCompat.INVALID_STREAM_TYPE;
 
 import android.media.AudioAttributes;
@@ -27,7 +27,7 @@
 
 /** @hide */
 @VersionedParcelize(jetifyAs = "android.support.v4.media.AudioAttributesImplApi26")
-@RestrictTo(LIBRARY_GROUP_PREFIX)
+@RestrictTo(LIBRARY)
 @RequiresApi(26)
 public class AudioAttributesImplApi26 extends AudioAttributesImplApi21 {
     private static final String TAG = "AudioAttributesCompat26";
diff --git a/media/src/main/java/androidx/media/AudioAttributesImplBase.java b/media/src/main/java/androidx/media/AudioAttributesImplBase.java
index 1750a12..6525ab6 100644
--- a/media/src/main/java/androidx/media/AudioAttributesImplBase.java
+++ b/media/src/main/java/androidx/media/AudioAttributesImplBase.java
@@ -16,7 +16,7 @@
 
 package androidx.media;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.media.AudioAttributesCompat.CONTENT_TYPE_MOVIE;
 import static androidx.media.AudioAttributesCompat.CONTENT_TYPE_MUSIC;
 import static androidx.media.AudioAttributesCompat.CONTENT_TYPE_SONIFICATION;
@@ -50,24 +50,16 @@
 
 /** @hide */
 @VersionedParcelize(jetifyAs = "android.support.v4.media.AudioAttributesImplBase")
-@RestrictTo(LIBRARY_GROUP_PREFIX)
+@RestrictTo(LIBRARY)
 public class AudioAttributesImplBase implements AudioAttributesImpl {
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
     @ParcelField(1)
-    public int mUsage = USAGE_UNKNOWN;
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    int mUsage = USAGE_UNKNOWN;
     @ParcelField(2)
-    public int mContentType = CONTENT_TYPE_UNKNOWN;
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    int mContentType = CONTENT_TYPE_UNKNOWN;
     @ParcelField(3)
-    public int mFlags = 0x0;
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    int mFlags = 0x0;
     @ParcelField(4)
-    public int mLegacyStream = INVALID_STREAM_TYPE;
+    int mLegacyStream = INVALID_STREAM_TYPE;
 
     /**
      * Used for VersionedParcelable
diff --git a/media/src/main/java/androidx/media/AudioFocusRequestCompat.java b/media/src/main/java/androidx/media/AudioFocusRequestCompat.java
index e932d63..8935faa 100644
--- a/media/src/main/java/androidx/media/AudioFocusRequestCompat.java
+++ b/media/src/main/java/androidx/media/AudioFocusRequestCompat.java
@@ -16,8 +16,6 @@
 
 package androidx.media;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
-
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.media.AudioAttributes;
@@ -33,7 +31,6 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
 import androidx.core.util.ObjectsCompat;
 
 import java.lang.annotation.Retention;
@@ -45,8 +42,6 @@
     /* package */ static final AudioAttributesCompat FOCUS_DEFAULT_ATTR =
             new AudioAttributesCompat.Builder().setUsage(AudioAttributesCompat.USAGE_MEDIA).build();
 
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Retention(SOURCE)
     @IntDef({
         AudioManagerCompat.AUDIOFOCUS_GAIN,
@@ -54,7 +49,7 @@
         AudioManagerCompat.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
         AudioManagerCompat.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
     })
-    public @interface FocusGainType {}
+    private @interface FocusGainType {}
 
     private final int mFocusGain;
     private final OnAudioFocusChangeListener mOnAudioFocusChangeListener;
@@ -392,10 +387,8 @@
          *
          * @param focusGain value to check
          * @return true if focusGain is a valid value for an audio focus request.
-         * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
-        static boolean isValidFocusGain(@FocusGainType int focusGain) {
+        private static boolean isValidFocusGain(@FocusGainType int focusGain) {
             switch (focusGain) {
                 case AudioManagerCompat.AUDIOFOCUS_GAIN:
                 case AudioManagerCompat.AUDIOFOCUS_GAIN_TRANSIENT:
diff --git a/media/src/main/java/androidx/media/MediaBrowserCompatUtils.java b/media/src/main/java/androidx/media/MediaBrowserCompatUtils.java
index 0ca95e8..2c48e8c 100644
--- a/media/src/main/java/androidx/media/MediaBrowserCompatUtils.java
+++ b/media/src/main/java/androidx/media/MediaBrowserCompatUtils.java
@@ -16,7 +16,7 @@
 
 package androidx.media;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.os.Bundle;
 import android.support.v4.media.MediaBrowserCompat;
@@ -26,7 +26,7 @@
 /**
  * @hide
  */
-@RestrictTo(LIBRARY_GROUP_PREFIX)
+@RestrictTo(LIBRARY)
 public class MediaBrowserCompatUtils {
     public static boolean areSameOptions(Bundle options1, Bundle options2) {
         if (options1 == options2) {
diff --git a/media/src/main/java/androidx/media/MediaBrowserProtocol.java b/media/src/main/java/androidx/media/MediaBrowserProtocol.java
index ddd3283..5326e1f 100644
--- a/media/src/main/java/androidx/media/MediaBrowserProtocol.java
+++ b/media/src/main/java/androidx/media/MediaBrowserProtocol.java
@@ -15,6 +15,8 @@
  */
 package androidx.media;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
 import android.os.Bundle;
 import android.support.v4.media.MediaBrowserCompat;
 
@@ -25,7 +27,7 @@
  *
  * @hide
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
+@RestrictTo(LIBRARY)
 public class MediaBrowserProtocol {
 
     public static final String DATA_CALLBACK_TOKEN = "data_callback_token";
diff --git a/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java b/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java
index 35d46b2..82ff4b9 100644
--- a/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java
+++ b/media/src/main/java/androidx/media/MediaBrowserServiceCompat.java
@@ -176,8 +176,6 @@
     @RestrictTo(LIBRARY)
     public static final int RESULT_PROGRESS_UPDATE = 1;
 
-    /** @hide */
-    @RestrictTo(LIBRARY)
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, value = {RESULT_FLAG_OPTION_NOT_HANDLED,
             RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED, RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED})
@@ -1304,7 +1302,7 @@
      *
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY_GROUP_PREFIX) // accessed by media2-session
     public void attachToBaseContext(Context base) {
         attachBaseContext(base);
     }
@@ -1422,7 +1420,7 @@
      * @param option option
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     public void onSubscribe(String id, Bundle option) {
     }
 
@@ -1432,7 +1430,7 @@
      * @param id
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     public void onUnsubscribe(String id) {
     }
 
@@ -1618,7 +1616,7 @@
      *            contain the information about the change.
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY_GROUP_PREFIX) // accessed by media2-session
     public void notifyChildrenChanged(@NonNull RemoteUserInfo remoteUserInfo,
             @NonNull String parentId, @NonNull Bundle options) {
         if (remoteUserInfo == null) {
diff --git a/media/src/main/java/androidx/media/MediaSessionManager.java b/media/src/main/java/androidx/media/MediaSessionManager.java
index 7183e02..b11f186 100644
--- a/media/src/main/java/androidx/media/MediaSessionManager.java
+++ b/media/src/main/java/androidx/media/MediaSessionManager.java
@@ -16,7 +16,7 @@
 
 package androidx.media;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.content.Context;
 import android.os.Build;
@@ -128,14 +128,14 @@
          * Represents an unknown pid of an application.
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         public static final int UNKNOWN_PID = -1;
 
         /**
          * Represents an unknown uid of an application.
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         public static final int UNKNOWN_UID = -1;
 
         RemoteUserInfoImpl mImpl;
@@ -169,7 +169,7 @@
          * @param remoteUserInfo Framework RemoteUserInfo
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         @RequiresApi(28)
         public RemoteUserInfo(
                 android.media.session.MediaSessionManager.RemoteUserInfo remoteUserInfo) {
diff --git a/media/src/main/java/androidx/media/VolumeProviderCompat.java b/media/src/main/java/androidx/media/VolumeProviderCompat.java
index eea4c7b..6d835d6 100644
--- a/media/src/main/java/androidx/media/VolumeProviderCompat.java
+++ b/media/src/main/java/androidx/media/VolumeProviderCompat.java
@@ -39,7 +39,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY_GROUP_PREFIX) // used by mediarouter
     @IntDef({VOLUME_CONTROL_FIXED, VOLUME_CONTROL_RELATIVE, VOLUME_CONTROL_ABSOLUTE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface ControlType {}
diff --git a/media/src/main/java/androidx/media/app/NotificationCompat.java b/media/src/main/java/androidx/media/app/NotificationCompat.java
index 9e6af2e..e1fe44d 100644
--- a/media/src/main/java/androidx/media/app/NotificationCompat.java
+++ b/media/src/main/java/androidx/media/app/NotificationCompat.java
@@ -16,7 +16,7 @@
 
 package androidx.media.app;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.core.app.NotificationCompat.COLOR_DEFAULT;
 
 import android.app.Notification;
@@ -204,7 +204,7 @@
         /**
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         @Override
         public void apply(NotificationBuilderWithBuilderAccessor builder) {
             if (Build.VERSION.SDK_INT >= 21) {
@@ -229,7 +229,7 @@
         /**
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         @Override
         public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {
             if (Build.VERSION.SDK_INT >= 21) {
@@ -297,7 +297,7 @@
         /**
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         @Override
         public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {
             if (Build.VERSION.SDK_INT >= 21) {
@@ -382,7 +382,7 @@
         /**
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         @Override
         public void apply(NotificationBuilderWithBuilderAccessor builder) {
             if (Build.VERSION.SDK_INT >= 24) {
@@ -396,7 +396,7 @@
         /**
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         @Override
         public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {
             if (Build.VERSION.SDK_INT >= 24) {
@@ -438,7 +438,7 @@
         /**
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         @Override
         public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {
             if (Build.VERSION.SDK_INT >= 24) {
@@ -470,7 +470,7 @@
         /**
          * @hide
          */
-        @RestrictTo(LIBRARY_GROUP_PREFIX)
+        @RestrictTo(LIBRARY)
         @Override
         public RemoteViews makeHeadsUpContentView(NotificationBuilderWithBuilderAccessor builder) {
             if (Build.VERSION.SDK_INT >= 24) {
diff --git a/media2/session/api/1.1.0-alpha01.txt b/media2/session/api/1.1.0-alpha01.txt
index 4d96ad8..d16e2fb 100644
--- a/media2/session/api/1.1.0-alpha01.txt
+++ b/media2/session/api/1.1.0-alpha01.txt
@@ -190,7 +190,6 @@
     method public java.util.List<androidx.media2.session.MediaSession.ControllerInfo!> getConnectedControllers();
     method public String getId();
     method public androidx.media2.common.SessionPlayer getPlayer();
-    method public android.support.v4.media.session.MediaSessionCompat getSessionCompat();
     method public androidx.media2.session.SessionToken getToken();
     method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> sendCustomCommand(androidx.media2.session.MediaSession.ControllerInfo, androidx.media2.session.SessionCommand, android.os.Bundle?);
     method public void setAllowedCommands(androidx.media2.session.MediaSession.ControllerInfo, androidx.media2.session.SessionCommandGroup);
diff --git a/media2/session/api/current.txt b/media2/session/api/current.txt
index 4d96ad8..d16e2fb 100644
--- a/media2/session/api/current.txt
+++ b/media2/session/api/current.txt
@@ -190,7 +190,6 @@
     method public java.util.List<androidx.media2.session.MediaSession.ControllerInfo!> getConnectedControllers();
     method public String getId();
     method public androidx.media2.common.SessionPlayer getPlayer();
-    method public android.support.v4.media.session.MediaSessionCompat getSessionCompat();
     method public androidx.media2.session.SessionToken getToken();
     method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> sendCustomCommand(androidx.media2.session.MediaSession.ControllerInfo, androidx.media2.session.SessionCommand, android.os.Bundle?);
     method public void setAllowedCommands(androidx.media2.session.MediaSession.ControllerInfo, androidx.media2.session.SessionCommandGroup);
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaSession.java b/media2/session/src/main/java/androidx/media2/session/MediaSession.java
index d601bd7..90664f5 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaSession.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaSession.java
@@ -386,12 +386,10 @@
     }
 
     /**
-     * Gets the {@link MediaSessionCompat} for this session to handle incoming commands from the
-     * {@link android.support.v4.media.session.MediaSessionCompat} for legacy support.
-     *
-     * @return MediaSessionCompat
+     * @hide
+     * @return Bundle
      */
-    @NonNull
+    @RestrictTo(LIBRARY_GROUP_PREFIX)
     public MediaSessionCompat getSessionCompat() {
         return mImpl.getSessionCompat();
     }
diff --git a/media2/session/src/main/java/androidx/media2/session/SessionTokenImplBase.java b/media2/session/src/main/java/androidx/media2/session/SessionTokenImplBase.java
index b0296b7..1db4c73 100644
--- a/media2/session/src/main/java/androidx/media2/session/SessionTokenImplBase.java
+++ b/media2/session/src/main/java/androidx/media2/session/SessionTokenImplBase.java
@@ -156,7 +156,7 @@
     }
 
     @Override
-    @NonNull
+    @Nullable
     public Bundle getExtras() {
         return mExtras;
     }
diff --git a/navigation/navigation-common/api/api_lint.ignore b/navigation/navigation-common/api/api_lint.ignore
index 59abda6..7fcf560 100644
--- a/navigation/navigation-common/api/api_lint.ignore
+++ b/navigation/navigation-common/api/api_lint.ignore
@@ -1,11 +1,3 @@
 // Baseline format: 1.0
 KotlinOperator: androidx.navigation.NavType#get(android.os.Bundle, String):
     Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: androidx.navigation.NavType.ParcelableArrayType#get(android.os.Bundle, String):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: androidx.navigation.NavType.ParcelableType#get(android.os.Bundle, String):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: androidx.navigation.NavType.SerializableArrayType#get(android.os.Bundle, String):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: androidx.navigation.NavType.SerializableType#get(android.os.Bundle, String):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
diff --git a/navigation/navigation-fragment-ktx/build.gradle b/navigation/navigation-fragment-ktx/build.gradle
index b588ff0..dd016f5 100644
--- a/navigation/navigation-fragment-ktx/build.gradle
+++ b/navigation/navigation-fragment-ktx/build.gradle
@@ -51,6 +51,7 @@
     androidTestImplementation(TRUTH)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
+    androidTestImplementation project(':internal-testutils')
 }
 
 androidx {
diff --git a/navigation/navigation-fragment-ktx/src/androidTest/AndroidManifest.xml b/navigation/navigation-fragment-ktx/src/androidTest/AndroidManifest.xml
index 70a80b1..151e7a8 100644
--- a/navigation/navigation-fragment-ktx/src/androidTest/AndroidManifest.xml
+++ b/navigation/navigation-fragment-ktx/src/androidTest/AndroidManifest.xml
@@ -19,5 +19,6 @@
           package="androidx.navigation.fragment.ktx">
     <application>
         <activity android:name="androidx.navigation.fragment.TestActivity" />
+        <activity android:name="androidx.navigation.fragment.NavGraphActivity" />
     </application>
 </manifest>
diff --git a/navigation/navigation-fragment-ktx/src/androidTest/java/androidx/navigation/fragment/NavGraphViewModelLazyTest.kt b/navigation/navigation-fragment-ktx/src/androidTest/java/androidx/navigation/fragment/NavGraphViewModelLazyTest.kt
index b124750..5a7eff9 100644
--- a/navigation/navigation-fragment-ktx/src/androidTest/java/androidx/navigation/fragment/NavGraphViewModelLazyTest.kt
+++ b/navigation/navigation-fragment-ktx/src/androidTest/java/androidx/navigation/fragment/NavGraphViewModelLazyTest.kt
@@ -22,19 +22,25 @@
 import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
 import androidx.fragment.app.testing.launchFragmentInContainer
+import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelStore
 import androidx.navigation.NavHostController
 import androidx.navigation.Navigation
+import androidx.navigation.findNavController
+import androidx.navigation.fragment.ktx.test.R
 import androidx.navigation.navGraphViewModels
 import androidx.navigation.navigation
 import androidx.navigation.plusAssign
 import androidx.navigation.testing.TestNavigator
 import androidx.navigation.testing.test
+import androidx.test.core.app.ActivityScenario
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
+import androidx.testutils.withActivity
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -57,21 +63,82 @@
             Navigation.setViewNavController(fragment.requireView(), navController)
         }
         val navGraph = navController.navigatorProvider.navigation(
-            id = GRAPH_ID,
-            startDestination = DESTINATION_ID
+            id = R.id.vm_graph,
+            startDestination = R.id.start_destination
         ) {
-            test(DESTINATION_ID)
+            test(R.id.start_destination)
         }
         navController.setGraph(navGraph, null)
 
         scenario.onFragment { fragment ->
             assertThat(fragment.viewModel).isNotNull()
+            assertThat(fragment.savedStateViewModel).isNotNull()
+        }
+    }
+
+    @Test
+    fun sameViewModelAcrossFragments() {
+        with(ActivityScenario.launch(NavGraphActivity::class.java)) {
+            val navController = withActivity { findNavController(R.id.nav_host_fragment) }
+            val firstFragment: TestVMFragment = withActivity {
+                val navHostFragment = supportFragmentManager
+                    .findFragmentById(R.id.nav_host_fragment)!!
+                navHostFragment.childFragmentManager.primaryNavigationFragment as TestVMFragment
+            }
+            val viewModel = firstFragment.viewModel
+            val savedStateViewModel = firstFragment.savedStateViewModel
+            assertThat(viewModel).isNotNull()
+            assertThat(savedStateViewModel).isNotNull()
+
+            // First assert that we don't have any default value since the
+            // navigation graph wasn't sent any arguments
+            val initialState: String? = savedStateViewModel.savedStateHandle["test"]
+            assertThat(initialState).isNull()
+
+            // Now set arguments
+            savedStateViewModel.savedStateHandle.set("test", "test")
+
+            // Navigate to the second destination and ensure it
+            // gets the same ViewModels and data
+            navController.navigate(R.id.second_destination)
+            val secondFragment: TestVMFragment = withActivity {
+                val navHostFragment = supportFragmentManager
+                    .findFragmentById(R.id.nav_host_fragment)!!
+                navHostFragment.childFragmentManager.primaryNavigationFragment as TestVMFragment
+            }
+            assertThat(secondFragment.viewModel)
+                .isSameInstanceAs(viewModel)
+            assertThat(secondFragment.savedStateViewModel)
+                .isSameInstanceAs(savedStateViewModel)
+            val savedValue: String? = secondFragment.savedStateViewModel
+                .savedStateHandle["test"]
+            assertThat(savedValue).isEqualTo("test")
+
+            // Now recreate the Activity and ensure that when we
+            // first request the nav graph ViewModel on the second destination
+            // that we get the same ViewModel and data back
+            recreate()
+            val recreatedFragment: TestVMFragment = withActivity {
+                val navHostFragment = supportFragmentManager
+                    .findFragmentById(R.id.nav_host_fragment)!!
+                navHostFragment.childFragmentManager.primaryNavigationFragment as TestVMFragment
+            }
+            assertThat(recreatedFragment.viewModel)
+                .isSameInstanceAs(viewModel)
+            assertThat(recreatedFragment.savedStateViewModel)
+                .isSameInstanceAs(savedStateViewModel)
+            val recreatedValue: String? = recreatedFragment.savedStateViewModel
+                .savedStateHandle["test"]
+            assertThat(recreatedValue).isEqualTo("test")
         }
     }
 }
 
+class NavGraphActivity : FragmentActivity(R.layout.activity_nav_graph)
+
 class TestVMFragment : Fragment() {
-    val viewModel: TestViewModel by navGraphViewModels(GRAPH_ID)
+    val viewModel: TestViewModel by navGraphViewModels(R.id.vm_graph)
+    val savedStateViewModel: TestSavedStateViewModel by navGraphViewModels(R.id.vm_graph)
     override fun onCreateView(
         inflater: LayoutInflater,
         container: ViewGroup?,
@@ -82,6 +149,4 @@
 }
 
 class TestViewModel : ViewModel()
-
-private const val GRAPH_ID = 1
-private const val DESTINATION_ID = 2
\ No newline at end of file
+class TestSavedStateViewModel(val savedStateHandle: SavedStateHandle) : ViewModel()
diff --git a/navigation/navigation-fragment-ktx/src/androidTest/res/layout/activity_nav_graph.xml b/navigation/navigation-fragment-ktx/src/androidTest/res/layout/activity_nav_graph.xml
new file mode 100644
index 0000000..b8cdfcfb
--- /dev/null
+++ b/navigation/navigation-fragment-ktx/src/androidTest/res/layout/activity_nav_graph.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<fragment
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/nav_host_fragment"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:name="androidx.navigation.fragment.NavHostFragment"
+    app:defaultNavHost="true"
+    app:navGraph="@navigation/vm_graph" />
diff --git a/navigation/navigation-fragment-ktx/src/androidTest/res/navigation/vm_graph.xml b/navigation/navigation-fragment-ktx/src/androidTest/res/navigation/vm_graph.xml
new file mode 100644
index 0000000..b8daf2b
--- /dev/null
+++ b/navigation/navigation-fragment-ktx/src/androidTest/res/navigation/vm_graph.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<navigation
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/vm_graph"
+    app:startDestination="@+id/start_destination">
+    <fragment
+        android:id="@+id/start_destination"
+        android:name="androidx.navigation.fragment.TestVMFragment">
+        <argument
+            android:name="test"
+            android:defaultValue="first"/>
+    </fragment>
+    <fragment
+        android:id="@+id/second_destination"
+        android:name="androidx.navigation.fragment.TestVMFragment">
+        <argument
+            android:name="test"
+            android:defaultValue="second"/>
+    </fragment>
+</navigation>
diff --git a/navigation/navigation-fragment-ktx/src/main/java/androidx/navigation/NavGraphViewModelLazy.kt b/navigation/navigation-fragment-ktx/src/main/java/androidx/navigation/NavGraphViewModelLazy.kt
index b4c0f16..75e7122 100644
--- a/navigation/navigation-fragment-ktx/src/main/java/androidx/navigation/NavGraphViewModelLazy.kt
+++ b/navigation/navigation-fragment-ktx/src/main/java/androidx/navigation/NavGraphViewModelLazy.kt
@@ -20,6 +20,7 @@
 import androidx.annotation.MainThread
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.createViewModelLazy
+import androidx.lifecycle.HasDefaultViewModelProviderFactory
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelStore
@@ -52,8 +53,14 @@
     @IdRes navGraphId: Int,
     noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null
 ): Lazy<VM> {
-    val storeProducer: () -> ViewModelStore = {
-        findNavController().getViewModelStoreOwner(navGraphId).viewModelStore
+    val viewModelStoreOwner by lazy {
+        findNavController().getViewModelStoreOwner(navGraphId)
     }
-    return createViewModelLazy(VM::class, storeProducer, factoryProducer)
+    val storeProducer: () -> ViewModelStore = {
+        viewModelStoreOwner.viewModelStore
+    }
+    return createViewModelLazy(VM::class, storeProducer, {
+        factoryProducer?.invoke() ?: (viewModelStoreOwner as HasDefaultViewModelProviderFactory)
+            .defaultViewModelProviderFactory
+    })
 }
\ No newline at end of file
diff --git a/navigation/navigation-runtime/build.gradle b/navigation/navigation-runtime/build.gradle
index 9afb788..2624b7c 100644
--- a/navigation/navigation-runtime/build.gradle
+++ b/navigation/navigation-runtime/build.gradle
@@ -30,6 +30,8 @@
     api(project(":navigation:navigation-common"))
     api(project(":activity:activity"))
     api(project(":lifecycle:lifecycle-viewmodel"))
+    api("androidx.savedstate:savedstate:1.0.0-rc01")
+    api(project(":lifecycle:lifecycle-viewmodel-savedstate"))
 
     androidTestImplementation(project(":navigation:navigation-testing"))
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
index 3b2698d..297e3d8 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
@@ -23,6 +23,8 @@
 import android.os.Parcel
 import android.os.Parcelable
 import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelStore
 import androidx.navigation.test.R
@@ -912,6 +914,39 @@
     }
 
     @Test
+    fun testGetViewModelStoreOwnerSavedStateViewModel() {
+        val hostStore = ViewModelStore()
+        val navController = createNavController()
+        navController.setViewModelStore(hostStore)
+        val navGraph = navController.navigatorProvider.navigation(
+            id = 1,
+            startDestination = R.id.start_test
+        ) {
+            test(R.id.start_test)
+        }
+        navController.setGraph(navGraph, null)
+
+        val owner = navController.getViewModelStoreOwner(navGraph.id)
+        assertThat(owner).isNotNull()
+        val viewModelProvider = ViewModelProvider(owner)
+        val viewModel = viewModelProvider[TestSavedStateViewModel::class.java]
+        assertThat(viewModel).isNotNull()
+        viewModel.savedStateHandle.set("test", "test")
+
+        val savedState = navController.saveState()
+        val restoredNavController = createNavController()
+        restoredNavController.setViewModelStore(hostStore)
+        restoredNavController.restoreState(savedState)
+        restoredNavController.graph = navGraph
+
+        val restoredOwner = navController.getViewModelStoreOwner(navGraph.id)
+        val restoredViewModel = ViewModelProvider(
+            restoredOwner)[TestSavedStateViewModel::class.java]
+        val restoredState: String? = restoredViewModel.savedStateHandle.get("test")
+        assertThat(restoredState).isEqualTo("test")
+    }
+
+    @Test
     fun testSaveRestoreGetViewModelStoreOwner() {
         val hostStore = ViewModelStore()
         val navController = createNavController()
@@ -990,6 +1025,8 @@
 
 class TestAndroidViewModel(application: Application) : AndroidViewModel(application)
 
+class TestSavedStateViewModel(val savedStateHandle: SavedStateHandle) : ViewModel()
+
 /**
  * [TestNavigator] that helps with testing saving and restoring state.
  */
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntry.java b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntry.java
index 0b9c4e5d..f226265 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntry.java
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntry.java
@@ -23,40 +23,65 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.lifecycle.HasDefaultViewModelProviderFactory;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.lifecycle.SavedStateViewModelFactory;
 import androidx.lifecycle.ViewModelProvider;
 import androidx.lifecycle.ViewModelStore;
 import androidx.lifecycle.ViewModelStoreOwner;
+import androidx.savedstate.SavedStateRegistry;
+import androidx.savedstate.SavedStateRegistryController;
+import androidx.savedstate.SavedStateRegistryOwner;
 
 import java.util.UUID;
 
 /**
  * Representation of an entry in the back stack of a {@link NavController}.
  */
-final class NavBackStackEntry implements ViewModelStoreOwner, HasDefaultViewModelProviderFactory {
+final class NavBackStackEntry implements
+        LifecycleOwner,
+        ViewModelStoreOwner, HasDefaultViewModelProviderFactory,
+        SavedStateRegistryOwner {
     private final Context mContext;
     private final NavDestination mDestination;
     private final Bundle mArgs;
+    private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
+    private final SavedStateRegistryController mSavedStateRegistryController =
+            SavedStateRegistryController.create(this);
 
     // Internal unique name for this navBackStackEntry;
     @NonNull
     final UUID mId;
     private NavControllerViewModel mNavControllerViewModel;
+    private ViewModelProvider.Factory mDefaultFactory;
 
     NavBackStackEntry(@NonNull Context context,
             @NonNull NavDestination destination, @Nullable Bundle args,
+            @Nullable LifecycleOwner navControllerLifecyleOwner,
             @Nullable NavControllerViewModel navControllerViewModel) {
-        this(context, destination, args, navControllerViewModel, UUID.randomUUID());
+        this(context, destination, args,
+                navControllerLifecyleOwner, navControllerViewModel,
+                UUID.randomUUID(), null);
     }
 
     NavBackStackEntry(@NonNull Context context,
             @NonNull NavDestination destination, @Nullable Bundle args,
+            @Nullable LifecycleOwner navControllerLifecyleOwner,
             @Nullable NavControllerViewModel navControllerViewModel,
-            @NonNull UUID uuid) {
+            @NonNull UUID uuid, @Nullable Bundle savedState) {
         mContext = context;
         mId = uuid;
         mDestination = destination;
         mArgs = args;
         mNavControllerViewModel = navControllerViewModel;
+        mSavedStateRegistryController.performRestore(savedState);
+        if (navControllerLifecyleOwner != null) {
+            mLifecycle.setCurrentState(navControllerLifecyleOwner.getLifecycle()
+                    .getCurrentState());
+        } else {
+            mLifecycle.setCurrentState(Lifecycle.State.CREATED);
+        }
     }
 
     /**
@@ -83,6 +108,16 @@
 
     @NonNull
     @Override
+    public Lifecycle getLifecycle() {
+        return mLifecycle;
+    }
+
+    void handleLifecycleEvent(Lifecycle.Event event) {
+        mLifecycle.handleLifecycleEvent(event);
+    }
+
+    @NonNull
+    @Override
     public ViewModelStore getViewModelStore() {
         return mNavControllerViewModel.getViewModelStore(mId);
     }
@@ -90,7 +125,22 @@
     @NonNull
     @Override
     public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
-        return ViewModelProvider.AndroidViewModelFactory.getInstance(
-                (Application) mContext.getApplicationContext());
+        if (mDefaultFactory == null) {
+            mDefaultFactory = new SavedStateViewModelFactory(
+                    (Application) mContext.getApplicationContext(),
+                    this,
+                    mArgs);
+        }
+        return mDefaultFactory;
+    }
+
+    @NonNull
+    @Override
+    public SavedStateRegistry getSavedStateRegistry() {
+        return mSavedStateRegistryController.getSavedStateRegistry();
+    }
+
+    void saveState(@NonNull Bundle outBundle) {
+        mSavedStateRegistryController.performSave(outBundle);
     }
 }
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.java b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.java
index a39a6e1..5dd3b7e 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.java
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.java
@@ -32,11 +32,14 @@
     private final UUID mUUID;
     private final int mDestinationId;
     private final Bundle mArgs;
+    private final Bundle mSavedState;
 
     NavBackStackEntryState(NavBackStackEntry entry) {
         mUUID = entry.mId;
         mDestinationId = entry.getDestination().getId();
         mArgs = entry.getArguments();
+        mSavedState = new Bundle();
+        entry.saveState(mSavedState);
     }
 
     @SuppressWarnings("WeakerAccess")
@@ -44,6 +47,7 @@
         mUUID = UUID.fromString(in.readString());
         mDestinationId = in.readInt();
         mArgs = in.readBundle(getClass().getClassLoader());
+        mSavedState = in.readBundle(getClass().getClassLoader());
     }
 
     @NonNull
@@ -60,6 +64,11 @@
         return mArgs;
     }
 
+    @NonNull
+    Bundle getSavedState() {
+        return mSavedState;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -70,6 +79,7 @@
         parcel.writeString(mUUID.toString());
         parcel.writeInt(mDestinationId);
         parcel.writeBundle(mArgs);
+        parcel.writeBundle(mSavedState);
     }
 
     public static final Parcelable.Creator<NavBackStackEntryState> CREATOR =
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.java b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.java
index 112e24e..9686153 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.java
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.java
@@ -33,6 +33,9 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.app.TaskStackBuilder;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleObserver;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.ViewModelStore;
 import androidx.lifecycle.ViewModelStoreOwner;
@@ -79,12 +82,14 @@
     private final Context mContext;
     private Activity mActivity;
     private NavInflater mInflater;
-    private NavGraph mGraph;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    NavGraph mGraph;
     private Bundle mNavigatorStateToRestore;
     private Parcelable[] mBackStackToRestore;
     private boolean mDeepLinkHandled;
 
-    private final Deque<NavBackStackEntry> mBackStack = new ArrayDeque<>();
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    final Deque<NavBackStackEntry> mBackStack = new ArrayDeque<>();
 
     private LifecycleOwner mLifecycleOwner;
     private NavControllerViewModel mViewModel;
@@ -94,6 +99,17 @@
     private final CopyOnWriteArrayList<OnDestinationChangedListener>
             mOnDestinationChangedListeners = new CopyOnWriteArrayList<>();
 
+    private final LifecycleObserver mLifecycleObserver = new LifecycleEventObserver() {
+        @Override
+        public void onStateChanged(@NonNull LifecycleOwner source,
+                @NonNull Lifecycle.Event event) {
+            if (mGraph != null) {
+                for (NavBackStackEntry entry : mBackStack) {
+                    entry.handleLifecycleEvent(event);
+                }
+            }
+        }
+    };
     private final OnBackPressedCallback mOnBackPressedCallback =
             new OnBackPressedCallback(false) {
         @Override
@@ -276,6 +292,7 @@
         for (Navigator<?> navigator : popOperations) {
             if (navigator.popBackStack()) {
                 NavBackStackEntry entry = mBackStack.removeLast();
+                entry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
                 if (mViewModel != null) {
                     mViewModel.clear(entry.mId);
                 }
@@ -480,8 +497,10 @@
                 if (args != null) {
                     args.setClassLoader(mContext.getClassLoader());
                 }
-                mBackStack.add(new NavBackStackEntry(mContext, node, args, mViewModel,
-                        state.getUUID()));
+                NavBackStackEntry entry = new NavBackStackEntry(mContext, node, args,
+                        mLifecycleOwner, mViewModel,
+                        state.getUUID(), state.getSavedState());
+                mBackStack.add(entry);
             }
             updateOnBackPressedCallbackEnabled();
             mBackStackToRestore = null;
@@ -865,7 +884,9 @@
             }
             // The mGraph should always be on the back stack after you navigate()
             if (mBackStack.isEmpty()) {
-                mBackStack.add(new NavBackStackEntry(mContext, mGraph, finalArgs, mViewModel));
+                NavBackStackEntry entry = new NavBackStackEntry(mContext, mGraph, finalArgs,
+                        mLifecycleOwner, mViewModel);
+                mBackStack.add(entry);
             }
             // Now ensure all intermediate NavGraphs are put on the back stack
             // to ensure that global actions work.
@@ -874,15 +895,16 @@
             while (destination != null && findDestination(destination.getId()) == null) {
                 NavGraph parent = destination.getParent();
                 if (parent != null) {
-                    hierarchy.addFirst(new NavBackStackEntry(mContext, parent, finalArgs,
-                            mViewModel));
+                    NavBackStackEntry entry = new NavBackStackEntry(mContext, parent, finalArgs,
+                            mLifecycleOwner, mViewModel);
+                    hierarchy.addFirst(entry);
                 }
                 destination = parent;
             }
             mBackStack.addAll(hierarchy);
             // And finally, add the new destination with its default args
             NavBackStackEntry newBackStackEntry = new NavBackStackEntry(mContext, newDest,
-                    newDest.addInDefaultArgs(finalArgs), mViewModel);
+                    newDest.addInDefaultArgs(finalArgs), mLifecycleOwner, mViewModel);
             mBackStack.add(newBackStackEntry);
         }
         updateOnBackPressedCallbackEnabled();
@@ -1004,6 +1026,7 @@
 
     void setLifecycleOwner(@NonNull LifecycleOwner owner) {
         mLifecycleOwner = owner;
+        mLifecycleOwner.getLifecycle().addObserver(mLifecycleObserver);
     }
 
     void setOnBackPressedDispatcher(@NonNull OnBackPressedDispatcher dispatcher) {
diff --git a/paging/common/api/api_lint.ignore b/paging/common/api/api_lint.ignore
index dec4b46..678d58d 100644
--- a/paging/common/api/api_lint.ignore
+++ b/paging/common/api/api_lint.ignore
@@ -23,10 +23,6 @@
     Parameter p references hidden type class androidx.paging.PositionalDataSource.InitialResult.
 
 
-KotlinOperator: androidx.paging.PagedList#get(int):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
-
-
 RegistrationName: androidx.paging.DataSource#addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback):
     Callback methods should be named register/unregister; was addInvalidatedCallback
 RegistrationName: androidx.paging.DataSource#addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit>):
diff --git a/paging/runtime/api/api_lint.ignore b/paging/runtime/api/api_lint.ignore
index 68bf03a..24e84aa 100644
--- a/paging/runtime/api/api_lint.ignore
+++ b/paging/runtime/api/api_lint.ignore
@@ -2,4 +2,4 @@
 DocumentExceptions: androidx.paging.AsyncPagedListDiffer#getItem(int):
     Method AsyncPagedListDiffer.getItem appears to be throwing java.lang.IndexOutOfBoundsException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
 DocumentExceptions: androidx.paging.AsyncPagedListDiffer#submitList(androidx.paging.PagedList<T>, Runnable):
-    Method AsyncPagedListDiffer.submitList appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+    Method AsyncPagedListDiffer.submitList appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
index 74cd10a..0d1f6d9 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
@@ -5140,11 +5140,13 @@
 
     void dispatchOnScrolled(int hresult, int vresult) {
         mDispatchScrollCounter++;
-        // Pass the current scrollX/scrollY values; no actual change in these properties occurred
-        // but some general-purpose code may choose to respond to changes this way.
+        // Pass the current scrollX/scrollY values as current values. No actual change in these
+        // properties occurred. Pass negative hresult and vresult as old values so that
+        // postSendViewScrolledAccessibilityEventCallback(l - oldl, t - oldt) in onScrollChanged
+        // sends the scrolled accessibility event correctly.
         final int scrollX = getScrollX();
         final int scrollY = getScrollY();
-        onScrollChanged(scrollX, scrollY, scrollX, scrollY);
+        onScrollChanged(scrollX, scrollY, scrollX - hresult, scrollY - vresult);
 
         // Pass the real deltas to onScrolled, the RecyclerView-specific method.
         onScrolled(hresult, vresult);
@@ -11354,7 +11356,9 @@
 
         @Override
         public String toString() {
-            final StringBuilder sb = new StringBuilder("ViewHolder{"
+            String className =
+                    getClass().isAnonymousClass() ? "ViewHolder" : getClass().getSimpleName();
+            final StringBuilder sb = new StringBuilder(className + "{"
                     + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
                     + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
             if (isScrap()) {
diff --git a/room/common/api/2.2.0-alpha02.ignore b/room/common/api/2.2.0-alpha02.ignore
index 64ce5cf..51b51d5 100644
--- a/room/common/api/2.2.0-alpha02.ignore
+++ b/room/common/api/2.2.0-alpha02.ignore
@@ -1,16 +1,4 @@
 // Baseline format: 1.0
-AddedAbstractMethod: androidx.room.ColumnInfo#defaultValue():
-    Added method androidx.room.ColumnInfo.defaultValue()
-AddedAbstractMethod: androidx.room.Delete#entity():
-    Added method androidx.room.Delete.entity()
-AddedAbstractMethod: androidx.room.Insert#entity():
-    Added method androidx.room.Insert.entity()
-AddedAbstractMethod: androidx.room.Relation#associateBy():
-    Added method androidx.room.Relation.associateBy()
-AddedAbstractMethod: androidx.room.Update#entity():
-    Added method androidx.room.Update.entity()
-
-
 ChangedType: androidx.room.Database#entities():
     Method androidx.room.Database.entities has changed return type from Class[] to Class<?>[]
 ChangedType: androidx.room.Database#views():
diff --git a/security/crypto/api/api_lint.ignore b/security/crypto/api/api_lint.ignore
index 2635915..0b681f2 100644
--- a/security/crypto/api/api_lint.ignore
+++ b/security/crypto/api/api_lint.ignore
@@ -5,10 +5,6 @@
     Context is distinct, so it must be the first argument (method `create`)
 
 
-KotlinOperator: androidx.security.crypto.EncryptedSharedPreferences#contains(String):
-    Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
-
-
 RegistrationName: androidx.security.crypto.EncryptedSharedPreferences#registerOnSharedPreferenceChangeListener(android.content.SharedPreferences.OnSharedPreferenceChangeListener):
     Listener methods should be named add/remove; was registerOnSharedPreferenceChangeListener
 RegistrationName: androidx.security.crypto.EncryptedSharedPreferences#unregisterOnSharedPreferenceChangeListener(android.content.SharedPreferences.OnSharedPreferenceChangeListener):
diff --git a/settings.gradle b/settings.gradle
index e953056..8e9171e 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -66,6 +66,8 @@
 includeProject(":benchmark:integration-tests:startup-benchmark", "benchmark/integration-tests/startup-benchmark")
 includeProject(":biometric", "biometric")
 includeProject(":browser", "browser")
+includeProject(":buildSrc-tests", "buildSrc-tests")
+includeProject(":buildSrc-tests:lint-checks", "buildSrc-tests/lint-checks")
 includeProject(":camera:camera-camera2", "camera/camera-camera2")
 includeProject(":camera:camera-core", "camera/camera-core")
 includeProject(":camera:camera-extensions", "camera/camera-extensions")
diff --git a/ui/integration-tests/benchmark/build.gradle b/ui/integration-tests/benchmark/build.gradle
index ab94dc5..44c1326 100644
--- a/ui/integration-tests/benchmark/build.gradle
+++ b/ui/integration-tests/benchmark/build.gradle
@@ -61,4 +61,3 @@
         useIR = true
     }
 }
-
diff --git a/ui/integration-tests/benchmark/src/androidTest/AndroidManifest.xml b/ui/integration-tests/benchmark/src/androidTest/AndroidManifest.xml
index 1496a9d..3369493 100644
--- a/ui/integration-tests/benchmark/src/androidTest/AndroidManifest.xml
+++ b/ui/integration-tests/benchmark/src/androidTest/AndroidManifest.xml
@@ -14,13 +14,16 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<manifest
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     package="androidx.ui.benchmark.test">
 
-    <!-- Important: disable debuggable for accurate performance results -->
+    <!--
+      ~ Important: disable debuggable for accurate performance results
+      ~ requestLegacyExternalStorage to enable legacy JSON reporting when targeting Q
+      -->
     <application
+        android:requestLegacyExternalStorage="true"
         android:debuggable="false"
         tools:replace="android:debuggable">
         <activity android:name="android.app.Activity"/>
diff --git a/ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt b/ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt
index c1a49d5..f3f6dd7 100644
--- a/ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt
+++ b/ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt
@@ -46,7 +46,7 @@
 fun <T> Transition(
     definition: TransitionDefinition<T>,
     toState: T,
-    @Children children: @Composable() (state: TransitionState) -> Unit
+    children: @Composable() (state: TransitionState) -> Unit
 ) {
     if (transitionsEnabled) {
         // TODO: This null is workaround for b/132148894
diff --git a/ui/ui-core/api/api_lint.ignore b/ui/ui-core/api/api_lint.ignore
index a0c8d75..c321320 100644
--- a/ui/ui-core/api/api_lint.ignore
+++ b/ui/ui-core/api/api_lint.ignore
@@ -106,17 +106,17 @@
 
 
 KotlinOperator: androidx.ui.engine.geometry.RRect#contains(androidx.ui.engine.geometry.Offset):
-    Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+    Note that adding the `operator` keyword would allow calling this method using operator syntax
 KotlinOperator: androidx.ui.engine.geometry.Rect#contains(androidx.ui.engine.geometry.Offset):
-    Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+    Note that adding the `operator` keyword would allow calling this method using operator syntax
 KotlinOperator: androidx.ui.engine.geometry.Size#contains(androidx.ui.engine.geometry.Offset):
-    Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+    Note that adding the `operator` keyword would allow calling this method using operator syntax
 KotlinOperator: androidx.ui.painting.Path#contains(androidx.ui.engine.geometry.Offset):
-    Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+    Note that adding the `operator` keyword would allow calling this method using operator syntax
 KotlinOperator: androidx.ui.text.TextRange#contains(androidx.ui.text.TextRange):
-    Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+    Note that adding the `operator` keyword would allow calling this method using operator syntax
 KotlinOperator: androidx.ui.text.TextRange#contains(int):
-    Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+    Note that adding the `operator` keyword would allow calling this method using operator syntax
 
 
 MethodNameUnits: androidx.ui.core.Durations#inSeconds(androidx.ui.core.Duration):
diff --git a/ui/ui-foundation/api/1.0.0-alpha01.txt b/ui/ui-foundation/api/1.0.0-alpha01.txt
index 5edc08e..ec7e422 100644
--- a/ui/ui-foundation/api/1.0.0-alpha01.txt
+++ b/ui/ui-foundation/api/1.0.0-alpha01.txt
@@ -29,43 +29,40 @@
 
 }
 
-package androidx.ui.foundation.gestures {
+package androidx.ui.foundation.animation {
 
-  public final class AnchorsFlingConfig implements androidx.ui.foundation.gestures.FlingConfig {
-    ctor public AnchorsFlingConfig(java.util.List<java.lang.Float> animationAnchors, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, androidx.animation.AnimationBuilder<java.lang.Float> animationBuilder, androidx.animation.DecayAnimation decayAnimation);
-    method public java.util.List<java.lang.Float> component1();
+  public final class AnimatedFloatDragController implements androidx.ui.foundation.gestures.DragValueController {
+    ctor public AnimatedFloatDragController(float initialValue, androidx.ui.foundation.animation.FlingConfig? flingConfig);
+    method public androidx.animation.AnimatedFloat getAnimatedFloat();
+    method public float getCurrentValue();
+    method public void onDrag(float target);
+    method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
+    method public void setBounds(float min, float max);
+    property public final androidx.animation.AnimatedFloat animatedFloat;
+    property public float currentValue;
+  }
+
+  public final class FlingConfig {
+    ctor public FlingConfig(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
+    ctor public FlingConfig();
+    method public androidx.animation.DecayAnimation component1();
     method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? component2();
-    method public androidx.animation.AnimationBuilder<java.lang.Float> component3();
-    method public androidx.animation.DecayAnimation component4();
-    method public androidx.ui.foundation.gestures.AnchorsFlingConfig copy(java.util.List<java.lang.Float> animationAnchors, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, androidx.animation.AnimationBuilder<java.lang.Float> animationBuilder, androidx.animation.DecayAnimation decayAnimation);
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
-    method public java.util.List<java.lang.Float> getAnimationAnchors();
-    method public androidx.animation.AnimationBuilder<java.lang.Float> getAnimationBuilder();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> component3();
+    method public androidx.ui.foundation.animation.FlingConfig copy(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> getAdjustTarget();
     method public androidx.animation.DecayAnimation getDecayAnimation();
     method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? getOnAnimationFinished();
   }
 
-  public final class AnimatedDraggableKt {
-    ctor public AnimatedDraggableKt();
-    method public static void AnimatedDraggable(androidx.ui.foundation.gestures.DragDirection dragDirection, float startValue, float minValue = Float.MIN_VALUE, float maxValue = Float.MAX_VALUE, androidx.ui.foundation.gestures.FlingConfig? flingConfig = null, kotlin.jvm.functions.Function1<? super androidx.animation.BaseAnimatedValue<java.lang.Float>,kotlin.Unit> children);
+  public final class FlingConfigKt {
+    ctor public FlingConfigKt();
+    method public static androidx.ui.foundation.animation.FlingConfig AnchorsFlingConfig(java.util.List<java.lang.Float> anchors, androidx.animation.AnimationBuilder<java.lang.Float> animationBuilder = PhysicsBuilder(), kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished = null, androidx.animation.DecayAnimation decayAnimation = ExponentialDecay());
+    method public static void fling(androidx.animation.AnimatedFloat, androidx.ui.foundation.animation.FlingConfig config, float startVelocity);
   }
 
-  public final class DecayFlingConfig implements androidx.ui.foundation.gestures.FlingConfig {
-    ctor public DecayFlingConfig(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onFlingFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
-    method public androidx.animation.DecayAnimation component1();
-    method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? component2();
-    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> component3();
-    method public androidx.ui.foundation.gestures.DecayFlingConfig copy(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onFlingFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
-    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> getAdjustTarget();
-    method public androidx.animation.DecayAnimation getDecayAnimation();
-    method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? getOnFlingFinished();
-  }
+}
 
-  public final class DefaultFlingConfig implements androidx.ui.foundation.gestures.FlingConfig {
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
-    field public static final androidx.ui.foundation.gestures.DefaultFlingConfig! INSTANCE;
-  }
+package androidx.ui.foundation.gestures {
 
   public abstract sealed class DragDirection {
   }
@@ -78,8 +75,33 @@
     field public static final androidx.ui.foundation.gestures.DragDirection.Vertical! INSTANCE;
   }
 
-  public interface FlingConfig {
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
+  public interface DragValueController {
+    method public float getCurrentValue();
+    method public void onDrag(float target);
+    method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
+    method public void setBounds(float min, float max);
+    property public abstract float currentValue;
+  }
+
+  public final class DraggableCallback {
+    ctor public DraggableCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onDragStarted, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onDragSettled);
+    ctor public DraggableCallback();
+  }
+
+  public final class DraggableKt {
+    ctor public DraggableKt();
+    method public static void Draggable(androidx.ui.foundation.gestures.DragDirection dragDirection, float minValue = Float.MIN_VALUE, float maxValue = Float.MAX_VALUE, androidx.ui.foundation.gestures.DragValueController valueController = +memo(minValue, { 
+    <init>(minValue)
+}), androidx.ui.foundation.gestures.DraggableCallback? callback = null, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> children);
+  }
+
+  public final class FloatDragValueController implements androidx.ui.foundation.gestures.DragValueController {
+    ctor public FloatDragValueController(float initialValue);
+    method public float getCurrentValue();
+    method public void onDrag(float target);
+    method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
+    method public void setBounds(float min, float max);
+    property public float currentValue;
   }
 
 }
diff --git a/ui/ui-foundation/api/current.txt b/ui/ui-foundation/api/current.txt
index 5edc08e..ec7e422 100644
--- a/ui/ui-foundation/api/current.txt
+++ b/ui/ui-foundation/api/current.txt
@@ -29,43 +29,40 @@
 
 }
 
-package androidx.ui.foundation.gestures {
+package androidx.ui.foundation.animation {
 
-  public final class AnchorsFlingConfig implements androidx.ui.foundation.gestures.FlingConfig {
-    ctor public AnchorsFlingConfig(java.util.List<java.lang.Float> animationAnchors, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, androidx.animation.AnimationBuilder<java.lang.Float> animationBuilder, androidx.animation.DecayAnimation decayAnimation);
-    method public java.util.List<java.lang.Float> component1();
+  public final class AnimatedFloatDragController implements androidx.ui.foundation.gestures.DragValueController {
+    ctor public AnimatedFloatDragController(float initialValue, androidx.ui.foundation.animation.FlingConfig? flingConfig);
+    method public androidx.animation.AnimatedFloat getAnimatedFloat();
+    method public float getCurrentValue();
+    method public void onDrag(float target);
+    method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
+    method public void setBounds(float min, float max);
+    property public final androidx.animation.AnimatedFloat animatedFloat;
+    property public float currentValue;
+  }
+
+  public final class FlingConfig {
+    ctor public FlingConfig(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
+    ctor public FlingConfig();
+    method public androidx.animation.DecayAnimation component1();
     method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? component2();
-    method public androidx.animation.AnimationBuilder<java.lang.Float> component3();
-    method public androidx.animation.DecayAnimation component4();
-    method public androidx.ui.foundation.gestures.AnchorsFlingConfig copy(java.util.List<java.lang.Float> animationAnchors, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, androidx.animation.AnimationBuilder<java.lang.Float> animationBuilder, androidx.animation.DecayAnimation decayAnimation);
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
-    method public java.util.List<java.lang.Float> getAnimationAnchors();
-    method public androidx.animation.AnimationBuilder<java.lang.Float> getAnimationBuilder();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> component3();
+    method public androidx.ui.foundation.animation.FlingConfig copy(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> getAdjustTarget();
     method public androidx.animation.DecayAnimation getDecayAnimation();
     method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? getOnAnimationFinished();
   }
 
-  public final class AnimatedDraggableKt {
-    ctor public AnimatedDraggableKt();
-    method public static void AnimatedDraggable(androidx.ui.foundation.gestures.DragDirection dragDirection, float startValue, float minValue = Float.MIN_VALUE, float maxValue = Float.MAX_VALUE, androidx.ui.foundation.gestures.FlingConfig? flingConfig = null, kotlin.jvm.functions.Function1<? super androidx.animation.BaseAnimatedValue<java.lang.Float>,kotlin.Unit> children);
+  public final class FlingConfigKt {
+    ctor public FlingConfigKt();
+    method public static androidx.ui.foundation.animation.FlingConfig AnchorsFlingConfig(java.util.List<java.lang.Float> anchors, androidx.animation.AnimationBuilder<java.lang.Float> animationBuilder = PhysicsBuilder(), kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished = null, androidx.animation.DecayAnimation decayAnimation = ExponentialDecay());
+    method public static void fling(androidx.animation.AnimatedFloat, androidx.ui.foundation.animation.FlingConfig config, float startVelocity);
   }
 
-  public final class DecayFlingConfig implements androidx.ui.foundation.gestures.FlingConfig {
-    ctor public DecayFlingConfig(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onFlingFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
-    method public androidx.animation.DecayAnimation component1();
-    method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? component2();
-    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> component3();
-    method public androidx.ui.foundation.gestures.DecayFlingConfig copy(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onFlingFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
-    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> getAdjustTarget();
-    method public androidx.animation.DecayAnimation getDecayAnimation();
-    method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? getOnFlingFinished();
-  }
+}
 
-  public final class DefaultFlingConfig implements androidx.ui.foundation.gestures.FlingConfig {
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
-    field public static final androidx.ui.foundation.gestures.DefaultFlingConfig! INSTANCE;
-  }
+package androidx.ui.foundation.gestures {
 
   public abstract sealed class DragDirection {
   }
@@ -78,8 +75,33 @@
     field public static final androidx.ui.foundation.gestures.DragDirection.Vertical! INSTANCE;
   }
 
-  public interface FlingConfig {
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
+  public interface DragValueController {
+    method public float getCurrentValue();
+    method public void onDrag(float target);
+    method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
+    method public void setBounds(float min, float max);
+    property public abstract float currentValue;
+  }
+
+  public final class DraggableCallback {
+    ctor public DraggableCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onDragStarted, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onDragSettled);
+    ctor public DraggableCallback();
+  }
+
+  public final class DraggableKt {
+    ctor public DraggableKt();
+    method public static void Draggable(androidx.ui.foundation.gestures.DragDirection dragDirection, float minValue = Float.MIN_VALUE, float maxValue = Float.MAX_VALUE, androidx.ui.foundation.gestures.DragValueController valueController = +memo(minValue, { 
+    <init>(minValue)
+}), androidx.ui.foundation.gestures.DraggableCallback? callback = null, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> children);
+  }
+
+  public final class FloatDragValueController implements androidx.ui.foundation.gestures.DragValueController {
+    ctor public FloatDragValueController(float initialValue);
+    method public float getCurrentValue();
+    method public void onDrag(float target);
+    method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
+    method public void setBounds(float min, float max);
+    property public float currentValue;
   }
 
 }
diff --git a/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt b/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
index 5edc08e..ec7e422 100644
--- a/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
@@ -29,43 +29,40 @@
 
 }
 
-package androidx.ui.foundation.gestures {
+package androidx.ui.foundation.animation {
 
-  public final class AnchorsFlingConfig implements androidx.ui.foundation.gestures.FlingConfig {
-    ctor public AnchorsFlingConfig(java.util.List<java.lang.Float> animationAnchors, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, androidx.animation.AnimationBuilder<java.lang.Float> animationBuilder, androidx.animation.DecayAnimation decayAnimation);
-    method public java.util.List<java.lang.Float> component1();
+  public final class AnimatedFloatDragController implements androidx.ui.foundation.gestures.DragValueController {
+    ctor public AnimatedFloatDragController(float initialValue, androidx.ui.foundation.animation.FlingConfig? flingConfig);
+    method public androidx.animation.AnimatedFloat getAnimatedFloat();
+    method public float getCurrentValue();
+    method public void onDrag(float target);
+    method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
+    method public void setBounds(float min, float max);
+    property public final androidx.animation.AnimatedFloat animatedFloat;
+    property public float currentValue;
+  }
+
+  public final class FlingConfig {
+    ctor public FlingConfig(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
+    ctor public FlingConfig();
+    method public androidx.animation.DecayAnimation component1();
     method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? component2();
-    method public androidx.animation.AnimationBuilder<java.lang.Float> component3();
-    method public androidx.animation.DecayAnimation component4();
-    method public androidx.ui.foundation.gestures.AnchorsFlingConfig copy(java.util.List<java.lang.Float> animationAnchors, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, androidx.animation.AnimationBuilder<java.lang.Float> animationBuilder, androidx.animation.DecayAnimation decayAnimation);
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
-    method public java.util.List<java.lang.Float> getAnimationAnchors();
-    method public androidx.animation.AnimationBuilder<java.lang.Float> getAnimationBuilder();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> component3();
+    method public androidx.ui.foundation.animation.FlingConfig copy(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> getAdjustTarget();
     method public androidx.animation.DecayAnimation getDecayAnimation();
     method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? getOnAnimationFinished();
   }
 
-  public final class AnimatedDraggableKt {
-    ctor public AnimatedDraggableKt();
-    method public static void AnimatedDraggable(androidx.ui.foundation.gestures.DragDirection dragDirection, float startValue, float minValue = Float.MIN_VALUE, float maxValue = Float.MAX_VALUE, androidx.ui.foundation.gestures.FlingConfig? flingConfig = null, kotlin.jvm.functions.Function1<? super androidx.animation.BaseAnimatedValue<java.lang.Float>,kotlin.Unit> children);
+  public final class FlingConfigKt {
+    ctor public FlingConfigKt();
+    method public static androidx.ui.foundation.animation.FlingConfig AnchorsFlingConfig(java.util.List<java.lang.Float> anchors, androidx.animation.AnimationBuilder<java.lang.Float> animationBuilder = PhysicsBuilder(), kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished = null, androidx.animation.DecayAnimation decayAnimation = ExponentialDecay());
+    method public static void fling(androidx.animation.AnimatedFloat, androidx.ui.foundation.animation.FlingConfig config, float startVelocity);
   }
 
-  public final class DecayFlingConfig implements androidx.ui.foundation.gestures.FlingConfig {
-    ctor public DecayFlingConfig(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onFlingFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
-    method public androidx.animation.DecayAnimation component1();
-    method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? component2();
-    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> component3();
-    method public androidx.ui.foundation.gestures.DecayFlingConfig copy(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onFlingFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
-    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> getAdjustTarget();
-    method public androidx.animation.DecayAnimation getDecayAnimation();
-    method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? getOnFlingFinished();
-  }
+}
 
-  public final class DefaultFlingConfig implements androidx.ui.foundation.gestures.FlingConfig {
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
-    field public static final androidx.ui.foundation.gestures.DefaultFlingConfig! INSTANCE;
-  }
+package androidx.ui.foundation.gestures {
 
   public abstract sealed class DragDirection {
   }
@@ -78,8 +75,33 @@
     field public static final androidx.ui.foundation.gestures.DragDirection.Vertical! INSTANCE;
   }
 
-  public interface FlingConfig {
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
+  public interface DragValueController {
+    method public float getCurrentValue();
+    method public void onDrag(float target);
+    method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
+    method public void setBounds(float min, float max);
+    property public abstract float currentValue;
+  }
+
+  public final class DraggableCallback {
+    ctor public DraggableCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onDragStarted, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onDragSettled);
+    ctor public DraggableCallback();
+  }
+
+  public final class DraggableKt {
+    ctor public DraggableKt();
+    method public static void Draggable(androidx.ui.foundation.gestures.DragDirection dragDirection, float minValue = Float.MIN_VALUE, float maxValue = Float.MAX_VALUE, androidx.ui.foundation.gestures.DragValueController valueController = +memo(minValue, { 
+    <init>(minValue)
+}), androidx.ui.foundation.gestures.DraggableCallback? callback = null, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> children);
+  }
+
+  public final class FloatDragValueController implements androidx.ui.foundation.gestures.DragValueController {
+    ctor public FloatDragValueController(float initialValue);
+    method public float getCurrentValue();
+    method public void onDrag(float target);
+    method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
+    method public void setBounds(float min, float max);
+    property public float currentValue;
   }
 
 }
diff --git a/ui/ui-foundation/api/restricted_current.txt b/ui/ui-foundation/api/restricted_current.txt
index 5edc08e..ec7e422 100644
--- a/ui/ui-foundation/api/restricted_current.txt
+++ b/ui/ui-foundation/api/restricted_current.txt
@@ -29,43 +29,40 @@
 
 }
 
-package androidx.ui.foundation.gestures {
+package androidx.ui.foundation.animation {
 
-  public final class AnchorsFlingConfig implements androidx.ui.foundation.gestures.FlingConfig {
-    ctor public AnchorsFlingConfig(java.util.List<java.lang.Float> animationAnchors, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, androidx.animation.AnimationBuilder<java.lang.Float> animationBuilder, androidx.animation.DecayAnimation decayAnimation);
-    method public java.util.List<java.lang.Float> component1();
+  public final class AnimatedFloatDragController implements androidx.ui.foundation.gestures.DragValueController {
+    ctor public AnimatedFloatDragController(float initialValue, androidx.ui.foundation.animation.FlingConfig? flingConfig);
+    method public androidx.animation.AnimatedFloat getAnimatedFloat();
+    method public float getCurrentValue();
+    method public void onDrag(float target);
+    method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
+    method public void setBounds(float min, float max);
+    property public final androidx.animation.AnimatedFloat animatedFloat;
+    property public float currentValue;
+  }
+
+  public final class FlingConfig {
+    ctor public FlingConfig(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
+    ctor public FlingConfig();
+    method public androidx.animation.DecayAnimation component1();
     method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? component2();
-    method public androidx.animation.AnimationBuilder<java.lang.Float> component3();
-    method public androidx.animation.DecayAnimation component4();
-    method public androidx.ui.foundation.gestures.AnchorsFlingConfig copy(java.util.List<java.lang.Float> animationAnchors, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, androidx.animation.AnimationBuilder<java.lang.Float> animationBuilder, androidx.animation.DecayAnimation decayAnimation);
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
-    method public java.util.List<java.lang.Float> getAnimationAnchors();
-    method public androidx.animation.AnimationBuilder<java.lang.Float> getAnimationBuilder();
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> component3();
+    method public androidx.ui.foundation.animation.FlingConfig copy(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
+    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> getAdjustTarget();
     method public androidx.animation.DecayAnimation getDecayAnimation();
     method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? getOnAnimationFinished();
   }
 
-  public final class AnimatedDraggableKt {
-    ctor public AnimatedDraggableKt();
-    method public static void AnimatedDraggable(androidx.ui.foundation.gestures.DragDirection dragDirection, float startValue, float minValue = Float.MIN_VALUE, float maxValue = Float.MAX_VALUE, androidx.ui.foundation.gestures.FlingConfig? flingConfig = null, kotlin.jvm.functions.Function1<? super androidx.animation.BaseAnimatedValue<java.lang.Float>,kotlin.Unit> children);
+  public final class FlingConfigKt {
+    ctor public FlingConfigKt();
+    method public static androidx.ui.foundation.animation.FlingConfig AnchorsFlingConfig(java.util.List<java.lang.Float> anchors, androidx.animation.AnimationBuilder<java.lang.Float> animationBuilder = PhysicsBuilder(), kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onAnimationFinished = null, androidx.animation.DecayAnimation decayAnimation = ExponentialDecay());
+    method public static void fling(androidx.animation.AnimatedFloat, androidx.ui.foundation.animation.FlingConfig config, float startVelocity);
   }
 
-  public final class DecayFlingConfig implements androidx.ui.foundation.gestures.FlingConfig {
-    ctor public DecayFlingConfig(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onFlingFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
-    method public androidx.animation.DecayAnimation component1();
-    method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? component2();
-    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> component3();
-    method public androidx.ui.foundation.gestures.DecayFlingConfig copy(androidx.animation.DecayAnimation decayAnimation, kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Boolean,kotlin.Unit>? onFlingFinished, kotlin.jvm.functions.Function1<? super java.lang.Float,androidx.animation.TargetAnimation> adjustTarget);
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
-    method public kotlin.jvm.functions.Function1<java.lang.Float,androidx.animation.TargetAnimation> getAdjustTarget();
-    method public androidx.animation.DecayAnimation getDecayAnimation();
-    method public kotlin.jvm.functions.Function2<java.lang.Float,java.lang.Boolean,kotlin.Unit>? getOnFlingFinished();
-  }
+}
 
-  public final class DefaultFlingConfig implements androidx.ui.foundation.gestures.FlingConfig {
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
-    field public static final androidx.ui.foundation.gestures.DefaultFlingConfig! INSTANCE;
-  }
+package androidx.ui.foundation.gestures {
 
   public abstract sealed class DragDirection {
   }
@@ -78,8 +75,33 @@
     field public static final androidx.ui.foundation.gestures.DragDirection.Vertical! INSTANCE;
   }
 
-  public interface FlingConfig {
-    method public void fling(androidx.animation.AnimatedFloat value, float startVelocity);
+  public interface DragValueController {
+    method public float getCurrentValue();
+    method public void onDrag(float target);
+    method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
+    method public void setBounds(float min, float max);
+    property public abstract float currentValue;
+  }
+
+  public final class DraggableCallback {
+    ctor public DraggableCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onDragStarted, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onDragSettled);
+    ctor public DraggableCallback();
+  }
+
+  public final class DraggableKt {
+    ctor public DraggableKt();
+    method public static void Draggable(androidx.ui.foundation.gestures.DragDirection dragDirection, float minValue = Float.MIN_VALUE, float maxValue = Float.MAX_VALUE, androidx.ui.foundation.gestures.DragValueController valueController = +memo(minValue, { 
+    <init>(minValue)
+}), androidx.ui.foundation.gestures.DraggableCallback? callback = null, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> children);
+  }
+
+  public final class FloatDragValueController implements androidx.ui.foundation.gestures.DragValueController {
+    ctor public FloatDragValueController(float initialValue);
+    method public float getCurrentValue();
+    method public void onDrag(float target);
+    method public void onDragEnd(float velocity, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueSettled);
+    method public void setBounds(float min, float max);
+    property public float currentValue;
   }
 
 }
diff --git a/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/AnimatedDraggableActivity.kt b/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/AnimatedDraggableActivity.kt
index d33545a..5f277c2 100644
--- a/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/AnimatedDraggableActivity.kt
+++ b/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/AnimatedDraggableActivity.kt
@@ -19,8 +19,12 @@
 import android.app.Activity
 import android.os.Bundle
 import androidx.compose.composer
+import androidx.ui.core.dp
 import androidx.ui.core.setContent
-import androidx.ui.foundation.samples.AnimatedDraggableSample
+import androidx.ui.foundation.samples.AnchoredDraggableSample
+import androidx.ui.foundation.samples.DraggableSample
+import androidx.ui.layout.Column
+import androidx.ui.layout.HeightSpacer
 import androidx.ui.layout.Wrap
 
 class AnimatedDraggableActivity : Activity() {
@@ -29,7 +33,11 @@
         super.onCreate(savedInstanceState)
         setContent {
             Wrap {
-                AnimatedDraggableSample()
+                Column {
+                    DraggableSample()
+                    HeightSpacer(100.dp)
+                    AnchoredDraggableSample()
+                }
             }
         }
     }
diff --git a/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/AnimatedDraggableSamples.kt b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/AnimatedDraggableSamples.kt
deleted file mode 100644
index 3714185..0000000
--- a/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/AnimatedDraggableSamples.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.ui.foundation.samples
-
-import androidx.annotation.Sampled
-import androidx.compose.Composable
-import androidx.compose.composer
-import androidx.compose.unaryPlus
-import androidx.ui.core.dp
-import androidx.ui.core.withDensity
-import androidx.ui.foundation.ColoredRect
-import androidx.ui.foundation.gestures.AnchorsFlingConfig
-import androidx.ui.foundation.gestures.AnimatedDraggable
-import androidx.ui.foundation.gestures.DragDirection
-import androidx.ui.foundation.shape.DrawShape
-import androidx.ui.foundation.shape.RectangleShape
-import androidx.ui.graphics.Color
-import androidx.ui.layout.Alignment
-import androidx.ui.layout.Container
-import androidx.ui.layout.Padding
-
-@Sampled
-@Composable
-fun AnimatedDraggableSample() {
-    // Composable that users can drag over 300 dp. There are 3 anchors
-    // and the value will gravitate to 0, 150 or 300 dp
-    val max = 300.dp
-    val min = 0.dp
-    val (minPx, maxPx) = +withDensity {
-        min.toPx().value to max.toPx().value
-    }
-
-    AnimatedDraggable(
-        dragDirection = DragDirection.Horizontal,
-        startValue = minPx,
-        minValue = minPx,
-        maxValue = maxPx,
-        // Specify an anchored behavior for the fling with anchors at max, min and center.
-        flingConfig = AnchorsFlingConfig(listOf(minPx, maxPx / 2, maxPx))
-    ) { dragValue ->
-
-        // dragValue is the AnimatedFloat with current value in progress
-        // of dragging or animating
-        val draggedDp = +withDensity {
-            dragValue.value.toDp()
-        }
-        val squareSize = 50.dp
-
-        // Draw a seekbar-like widget that has a black background
-        // with a red square that moves along the drag
-        Container(width = max + squareSize, alignment = Alignment.CenterLeft) {
-            DrawShape(RectangleShape, Color.Black)
-            Padding(left = draggedDp) {
-                ColoredRect(Color.Red, width = squareSize, height = squareSize)
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/DraggableSamples.kt b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/DraggableSamples.kt
new file mode 100644
index 0000000..8706d04
--- /dev/null
+++ b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/DraggableSamples.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.foundation.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.Composable
+import androidx.compose.composer
+import androidx.compose.memo
+import androidx.compose.unaryPlus
+import androidx.ui.animation.animatedFloat
+import androidx.ui.core.dp
+import androidx.ui.core.withDensity
+import androidx.ui.foundation.ColoredRect
+import androidx.ui.foundation.animation.AnchorsFlingConfig
+import androidx.ui.foundation.animation.AnimatedFloatDragController
+import androidx.ui.foundation.gestures.DragDirection
+import androidx.ui.foundation.gestures.Draggable
+import androidx.ui.foundation.shape.DrawShape
+import androidx.ui.foundation.shape.RectangleShape
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Alignment
+import androidx.ui.layout.Container
+import androidx.ui.layout.Padding
+
+@Sampled
+@Composable
+fun DraggableSample() {
+    // Composable that users can drag over 300 dp.
+    val max = 300.dp
+    val min = 0.dp
+    val (minPx, maxPx) = +withDensity {
+        min.toPx().value to max.toPx().value
+    }
+    Draggable(DragDirection.Horizontal, minPx, maxPx) { dragValue ->
+        // dragValue is the current value in progress of dragging
+        val draggedDp = +withDensity {
+            dragValue.toDp()
+        }
+        val squareSize = 50.dp
+
+        // Draw a seekbar-like widget that has a black background
+        // with a red square that moves along the drag
+        Container(width = max + squareSize, alignment = Alignment.CenterLeft) {
+            DrawShape(RectangleShape, Color.Black)
+            Padding(left = draggedDp) {
+                ColoredRect(Color.Red, width = squareSize, height = squareSize)
+            }
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun AnchoredDraggableSample() {
+    // Composable that users can drag over 300 dp. There are 3 anchors
+    // and the value will gravitate to 0, 150 or 300 dp
+    val max = 300.dp
+    val min = 0.dp
+    val (minPx, maxPx) = +withDensity {
+        min.toPx().value to max.toPx().value
+    }
+    // define anchors and related animation controller
+    val anchors = listOf(minPx, maxPx, maxPx / 2)
+    val flingConfig = +memo { AnchorsFlingConfig(anchors) }
+    val dragController = +memo { AnimatedFloatDragController(minPx, flingConfig) }
+
+    Draggable(
+        dragDirection = DragDirection.Horizontal,
+        valueController = dragController,
+        minValue = minPx,
+        maxValue = maxPx
+    ) { dragValue ->
+        // dragValue is the current value in progress
+        // of dragging or animation
+        val draggedDp = +withDensity {
+            dragValue.toDp()
+        }
+        val squareSize = 50.dp
+        // Draw a seekbar-like widget that has a black background
+        // with a red square that moves along the drag
+        Container(width = max + squareSize, alignment = Alignment.CenterLeft) {
+            DrawShape(RectangleShape, Color.Black)
+            Padding(left = draggedDp) {
+                ColoredRect(Color.Red, width = squareSize, height = squareSize)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Clickable.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Clickable.kt
index 1730ce1..bee68c7 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Clickable.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Clickable.kt
@@ -40,7 +40,7 @@
 fun Clickable(
     onClick: (() -> Unit)? = null,
     consumeDownOnStart: Boolean = false,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     Semantics(
         button = true,
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/DeterminateProgressIndicator.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/DeterminateProgressIndicator.kt
index 1182bfe..a40b834 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/DeterminateProgressIndicator.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/DeterminateProgressIndicator.kt
@@ -36,7 +36,7 @@
 @Composable
 fun DeterminateProgressIndicator(
     @FloatRange(from = 0.0, to = 1.0) progress: Float,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     if (progress !in 0f..1f) {
         throw IllegalArgumentException("Progress must be between 0.0 and 1.0")
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt
index 8ccc5b6..b6ad191 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt
@@ -47,7 +47,7 @@
  * @param children The content to be displayed inside the dialog.
  */
 @Composable
-fun Dialog(onCloseRequest: () -> Unit, @Children children: @Composable() () -> Unit) {
+fun Dialog(onCloseRequest: () -> Unit, children: @Composable() () -> Unit) {
     val context = +ambient(ContextAmbient)
 
     val dialog = +memo { DialogWrapper(context, onCloseRequest) }
@@ -72,7 +72,7 @@
         setContentView(frameLayout)
     }
 
-    fun setContent(@Children children: @Composable() () -> Unit) {
+    fun setContent(children: @Composable() () -> Unit) {
         frameLayout.setContent(children)
     }
 
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/animation/AnimatedFloatDragController.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/animation/AnimatedFloatDragController.kt
new file mode 100644
index 0000000..c47f27e
--- /dev/null
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/animation/AnimatedFloatDragController.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.foundation.animation
+
+import androidx.animation.AnimatedFloat
+import androidx.animation.ValueHolder
+import androidx.compose.Model
+import androidx.ui.foundation.gestures.DragValueController
+import androidx.ui.foundation.gestures.Draggable
+import androidx.ui.lerp
+
+/**
+ * Controller that proxy all dragging events to [AnimatedFloat] and [FlingConfig]
+ *
+ * It makes it possible to have animation support for [Draggable] composable
+ * as well as to have fling animation after drag has ended, which is defined by [FlingConfig]
+ *
+ * @param initialValue initial value for AnimatedFloat to set it up
+ * @param flingConfig sets behavior of the fling after drag has ended.
+ * Default is null, which means no fling will occur no matter the velocity
+ */
+class AnimatedFloatDragController(
+    initialValue: Float,
+    private val flingConfig: FlingConfig? = null
+) : DragValueController {
+
+    val animatedFloat = AnimatedFloat(AnimValueHolder(initialValue, ::lerp))
+
+    override val currentValue
+        get() = animatedFloat.value
+
+    override fun setBounds(min: Float, max: Float) = animatedFloat.setBounds(min, max)
+
+    override fun onDrag(target: Float) {
+        animatedFloat.snapTo(target)
+    }
+
+    override fun onDragEnd(velocity: Float, onValueSettled: (Float) -> Unit) {
+        if (flingConfig != null) {
+            val config =
+                flingConfig.copy(onAnimationFinished = { value: Float, cancelled: Boolean ->
+                    if (!cancelled) onValueSettled(value)
+                    flingConfig.onAnimationFinished?.invoke(value, cancelled)
+                })
+            animatedFloat.fling(config, velocity)
+        } else {
+            onValueSettled(animatedFloat.value)
+        }
+    }
+}
+
+@Model
+private class AnimValueHolder<T>(
+    override var value: T,
+    override val interpolator: (T, T, Float) -> T
+) : ValueHolder<T>
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/animation/FlingConfig.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/animation/FlingConfig.kt
new file mode 100644
index 0000000..52e3d5e
--- /dev/null
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/animation/FlingConfig.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.foundation.animation
+
+import androidx.animation.AnimatedFloat
+import androidx.animation.AnimationBuilder
+import androidx.animation.DecayAnimation
+import androidx.animation.ExponentialDecay
+import androidx.animation.PhysicsBuilder
+import androidx.animation.TargetAnimation
+import androidx.animation.fling
+import kotlin.math.abs
+
+/**
+ * Class to specify fling behavior.
+ *
+ * When drag has ended, this class specifies what to do given the velocity
+ * with which drag ended and AnimatedFloat instance to perform fling on and read current value.
+ *
+ * Config that provides natural fling with customizable behaviour
+ * e.g fling friction or result target adjustment.
+ *
+ * The most common Decay animation is [ExponentialDecay].
+ *
+ * If you want to only be able to drag/animate between predefined set of values,
+ * consider using [AnchorsFlingConfig] function to generate such behaviour.
+ *
+ * @param decayAnimation the animation to control fling behaviour
+ * @param onAnimationFinished callback to be invoked when fling finishes by decay
+ * or being interrupted by gesture input.
+ * Consider second boolean param "cancelled" to know what happened.
+ * @param adjustTarget callback to be called at the start of fling
+ * so the final value for fling can be adjusted
+ */
+data class FlingConfig(
+    val decayAnimation: DecayAnimation = ExponentialDecay(),
+    val onAnimationFinished: ((finalValue: Float, cancelled: Boolean) -> Unit)? = null,
+    val adjustTarget: (Float) -> TargetAnimation? = { null }
+)
+
+/**
+ * Starts a fling animation with the specified starting velocity and fling configuration.
+ *
+ * @param config configuration that specifies fling behaviour
+ * @param startVelocity Starting velocity of the fling animation
+ */
+fun AnimatedFloat.fling(config: FlingConfig, startVelocity: Float) {
+    fling(
+        startVelocity,
+        config.decayAnimation,
+        config.adjustTarget,
+        { config.onAnimationFinished?.invoke(value, it) }
+    )
+}
+
+/**
+ * Create fling config with anchors will make sure that after drag has ended,
+ * the value will be animated to one of the points from the predefined list.
+ *
+ * It takes velocity into account, though value will be animated to the closest
+ * point in provided list considering velocity.
+ *
+ * @param animationAnchors set of anchors to animate to
+ * @param onAnimationFinished callback to be invoked when animation value reaches desired anchor
+ * or fling being interrupted by gesture input.
+ * Consider the second boolean param "cancelled" to know what happened.
+ * @param animationBuilder animation which will be used for animations
+ * @param decayAnimation decay animation to be used to calculate closest point in the anchors set
+ * considering velocity.
+ */
+fun AnchorsFlingConfig(
+    anchors: List<Float>,
+    animationBuilder: AnimationBuilder<Float> = PhysicsBuilder(),
+    onAnimationFinished: ((finalValue: Float, cancelled: Boolean) -> Unit)? = null,
+    decayAnimation: DecayAnimation = ExponentialDecay()
+): FlingConfig {
+    val adjustTarget: (Float) -> TargetAnimation? = { target ->
+        val point = anchors.minBy { abs(it - target) }
+        val adjusted = point ?: target
+        TargetAnimation(adjusted, animationBuilder)
+    }
+    return FlingConfig(decayAnimation, onAnimationFinished, adjustTarget)
+}
\ No newline at end of file
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/AnimatedDraggable.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/AnimatedDraggable.kt
deleted file mode 100644
index 39e47e1..0000000
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/AnimatedDraggable.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.ui.foundation.gestures
-
-import androidx.animation.AnimatedFloat
-import androidx.animation.BaseAnimatedValue
-import androidx.compose.Composable
-import androidx.compose.composer
-import androidx.compose.unaryPlus
-import androidx.ui.animation.animatedFloat
-import androidx.ui.core.PxPosition
-import androidx.ui.core.gesture.DragGestureDetector
-import androidx.ui.core.gesture.DragObserver
-import androidx.ui.core.px
-
-/**
- * Component that provides drag, fling and animation logic for one [Float] value.
- *
- * The common usecase for this component is when you need to be able to drag/scroll something
- * on the screen and also one or more of the following:
- * 1. Fling support when scrolling/dragging ends with velocity.
- * 2. Stable anchors points support for dragging value,
- * e.g. be able to drag between only predefined set of values.
- * 3. Automatic animation of draggable value, e.g emulate drag by click.
- *
- * @see [FlingConfig] to control anchors or fling intensity.
- *
- * This component provides high-level API and ownership for [AnimatedFloat]
- * and returns it as a parameter for its children.
- *
- * If you need only drag support without animations, consider using [DragGestureDetector] instead.
- *
- * If you need only animations without gesture support, consider using [animatedFloat] instead.
- *
- * @sample androidx.ui.foundation.samples.AnimatedDraggableSample
- *
- * @param dragDirection direction in which drag should be happening
- * @param startValue value to set as initial for draggable/animating value in this component
- * @param minValue lower bound for draggable/animating value in this component
- * @param maxValue upper bound for draggable/animating value in this component
- * Either [DragDirection.Vertical] or [DragDirection.Horizontal]
- * @param flingConfig sets behavior of the fling after drag has ended.
- * Default is null, which means no drag will occur no matter the velocity
- */
-@Composable
-fun AnimatedDraggable(
-    dragDirection: DragDirection,
-    startValue: Float,
-    minValue: Float = Float.MIN_VALUE,
-    maxValue: Float = Float.MAX_VALUE,
-    flingConfig: FlingConfig? = null,
-    children: @Composable() (BaseAnimatedValue<Float>) -> Unit
-) {
-    val animFloat = (+animatedFloat(startValue)).apply {
-        setBounds(minValue, maxValue)
-    }
-    DragGestureDetector(
-        canDrag = { direction ->
-            dragDirection.isDraggableInDirection(direction, minValue, animFloat.value, maxValue)
-        },
-        dragObserver = object : DragObserver {
-
-            override fun onDrag(dragDistance: PxPosition): PxPosition {
-                val projected = dragDirection.project(dragDistance)
-                val newValue = (animFloat.value + projected).coerceIn(minValue, maxValue)
-                val consumed = newValue - animFloat.value
-                animFloat.snapTo(newValue)
-                val fractionConsumed = if (projected == 0f) 0f else consumed / projected
-                return PxPosition(
-                    dragDirection.xProjection(dragDistance.x).px * fractionConsumed,
-                    dragDirection.yProjection(dragDistance.y).px * fractionConsumed
-                )
-            }
-
-            override fun onStop(velocity: PxPosition) {
-                val projected = dragDirection.project(velocity)
-                flingConfig?.fling(animFloat, projected)
-            }
-        }
-    ) {
-        children(animFloat)
-    }
-}
-
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/DragDirection.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/DragDirection.kt
index abd6b48..4c16ec7 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/DragDirection.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/DragDirection.kt
@@ -21,7 +21,7 @@
 import androidx.ui.core.PxPosition
 
 /**
- * Draggable Direction specifies the direction in which you can drag an [AnimatedDraggable].
+ * Draggable Direction specifies the direction in which you can drag an [Draggable].
  * It can be either [Horizontal] or [Vertical].
  */
 sealed class DragDirection {
@@ -39,7 +39,7 @@
     internal open fun project(pos: PxPosition) = xProjection(pos.x) + yProjection(pos.y)
 
     /**
-     * Horizontal direction of dragging in [AnimatedDraggable].
+     * Horizontal direction of dragging in [Draggable].
      */
     object Horizontal : DragDirection() {
         internal override val xProjection: (Px) -> Float = { it.value }
@@ -60,7 +60,7 @@
     }
 
     /**
-     * Vertical direction of dragging in [AnimatedDraggable].
+     * Vertical direction of dragging in [Draggable].
      */
     object Vertical : DragDirection() {
         internal override val xProjection: (Px) -> Float = { 0f }
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/DragValueController.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/DragValueController.kt
new file mode 100644
index 0000000..fcf668f
--- /dev/null
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/DragValueController.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.foundation.gestures
+
+import androidx.compose.Model
+
+/**
+ * Interface that defines behaviour of value that is dragged via [Draggable]
+ */
+interface DragValueController {
+    /**
+     * Current Float value of controlled value
+     */
+    val currentValue: Float
+
+    /**
+     * Perform drag to [target] on controlled value
+     *
+     * @param target the resulted position for controlled value
+     */
+    fun onDrag(target: Float)
+
+    /**
+     * Perform finishing activities when drag has ended with given velocity
+     *
+     * This is a good place to start fling or consume velocity in some other way
+     * if you need to do so.
+     *
+     * Callback passed to this function *must* be called after controlled value reached it's final
+     * destination, e.g. it might be an immediate drag end or the end of the fling.
+     *
+     * @param velocity the velocity value when drag has ended
+     * @param onValueSettled callback to call after fling has ended and controlled value settled
+     */
+    fun onDragEnd(velocity: Float, onValueSettled: (Float) -> Unit)
+
+    /**
+     * Set bounds for controlled value
+     *
+     * @param min lower bound for dragging
+     * @param max upper bound for dragging
+     */
+    fun setBounds(min: Float, max: Float)
+}
+
+@Model
+private class FloatValueHolder(var inner: Float)
+
+/**
+ * Simple [DragValueController] that backs up single [Float] value with no fling support
+ *
+ * @param initialValue the initial value to set for controlled Float value
+ */
+class FloatDragValueController(initialValue: Float) : DragValueController {
+    override val currentValue: Float
+        get() = value.inner
+
+    private val value = FloatValueHolder(initialValue)
+    private var minBound = Float.MIN_VALUE
+    private var maxBound = Float.MAX_VALUE
+
+    override fun onDrag(target: Float) {
+        this.value.inner = target.coerceIn(minBound, maxBound)
+    }
+
+    override fun onDragEnd(velocity: Float, onValueSettled: (Float) -> Unit) {
+        onValueSettled(currentValue)
+    }
+
+    override fun setBounds(min: Float, max: Float) {
+        val changed = minBound != min || maxBound != max
+        minBound = min
+        maxBound = max
+        if (changed) value.inner = value.inner.coerceIn(minBound, maxBound)
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/Draggable.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/Draggable.kt
new file mode 100644
index 0000000..5fbcfc9
--- /dev/null
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/Draggable.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.foundation.gestures
+
+import androidx.compose.Composable
+import androidx.compose.composer
+import androidx.compose.memo
+import androidx.compose.unaryPlus
+import androidx.ui.core.PxPosition
+import androidx.ui.foundation.animation.AnimatedFloatDragController
+import androidx.ui.foundation.animation.AnchorsFlingConfig
+import androidx.ui.core.gesture.DragGestureDetector
+import androidx.ui.core.gesture.DragObserver
+import androidx.ui.core.px
+
+/**
+ * Component that provides high-level drag functionality reflected in one value
+ *
+ * The common usecase for this component is when you need to be able to drag/scroll something
+ * on the screen and represent it as one value via [DragValueController].
+ *
+ * If you need to control the whole dragging flow, consider using [DragGestureDetector] instead.
+ *
+ * @sample androidx.ui.foundation.samples.DraggableSample
+ *
+ * By using [AnimatedFloatDragController] with [AnchorsFlingConfig] you can achieve behaviour
+ * when value is gravitating to predefined set of points after drag has ended.
+ *
+ * @sample androidx.ui.foundation.samples.AnchoredDraggableSample
+ *
+ * @param dragDirection direction in which drag should be happening.
+ * Either [DragDirection.Vertical] or [DragDirection.Horizontal]
+ * @param minValue lower bound for draggable value in this component
+ * @param maxValue upper bound for draggable value in this component
+ * @param valueController controller to control value and how it will consume drag events,
+ * such as drag, fling or change of dragging bounds. The default is [FloatDragValueController],
+ * which provides simple move-as-much-as-user-drags login with no fling support.
+ * @param callback callback to react to drag events
+ */
+@Composable
+fun Draggable(
+    dragDirection: DragDirection,
+    minValue: Float = Float.MIN_VALUE,
+    maxValue: Float = Float.MAX_VALUE,
+    valueController: DragValueController = +memo(minValue) { FloatDragValueController(minValue) },
+    callback: DraggableCallback? = null,
+    children: @Composable() (Float) -> Unit
+) {
+    fun current() = valueController.currentValue
+    +memo(valueController, minValue, maxValue) {
+        valueController.setBounds(minValue, maxValue)
+    }
+    DragGestureDetector(
+        canDrag = { direction ->
+            dragDirection.isDraggableInDirection(direction, minValue, current(), maxValue)
+        },
+        dragObserver = object : DragObserver {
+
+            override fun onDrag(dragDistance: PxPosition): PxPosition {
+                callback?.notifyDrag()
+                val projected = dragDirection.project(dragDistance)
+                val newValue = (current() + projected).coerceIn(minValue, maxValue)
+                val consumed = newValue - current()
+                valueController.onDrag(newValue)
+                val fractionConsumed = if (projected == 0f) 0f else consumed / projected
+                return PxPosition(
+                    dragDirection.xProjection(dragDistance.x).px * fractionConsumed,
+                    dragDirection.yProjection(dragDistance.y).px * fractionConsumed
+                )
+            }
+
+            override fun onStop(velocity: PxPosition) {
+                val projected = dragDirection.project(velocity)
+                valueController.onDragEnd(projected) {
+                    callback?.notifyFinished(it)
+                }
+            }
+        }
+    ) {
+        children(current())
+    }
+}
+
+
+class DraggableCallback(
+    private val onDragStarted: () -> Unit = {},
+    private val onDragSettled: (Float) -> Unit = {}
+) {
+    private var startNotified: Boolean = false
+    internal fun notifyDrag() {
+        if (!startNotified) {
+            startNotified = true
+            onDragStarted()
+        }
+    }
+
+    internal fun notifyFinished(final: Float) {
+        startNotified = false
+        onDragSettled(final)
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/FlingConfig.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/FlingConfig.kt
deleted file mode 100644
index f2b41be..0000000
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/gestures/FlingConfig.kt
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.ui.foundation.gestures
-
-import androidx.animation.AnimatedFloat
-import androidx.animation.AnimationBuilder
-import androidx.animation.DecayAnimation
-import androidx.animation.ExponentialDecay
-import androidx.animation.PhysicsBuilder
-import androidx.animation.TargetAnimation
-import androidx.animation.fling
-import kotlin.math.abs
-
-/**
- * Interface to specify fling behavior in [AnimatedDraggable].
- *
- * When drag has ended, this class specifies what to do given the velocity
- * with which drag ended and AnimatedFloat instance to perform fling on and read current value.
- *
- * If you need natural fling support, use [DefaultFlingConfig] or
- * [DecayFlingConfig] to control how much friction is applied to the fling
- *
- * If you want to only be able to drag/animate between predefined set of values,
- * consider using [AnchorsFlingConfig].
- *
- */
-interface FlingConfig {
-    fun fling(value: AnimatedFloat, startVelocity: Float)
-}
-
-/**
- * Fling config with anchors will make sure that after drag has ended,
- * the value will be animated to one of the points from the predefined list.
- *
- * It takes velocity into account, though value will be animated to the closest
- * point in provided list considering velocity.
- *
- * @see ExponentialDecay to understand when to pass your own decayAnimation.
- *
- * @param animationAnchors set of anchors to animate to
- * @param onAnimationFinished callback to be invoked when animation value reaches desired anchor
- * or fling being interrupted by gesture input.
- * Consider the second boolean param "cancelled" to know what happened.
- * @param animationBuilder animation which will be used for animations
- * @param decayAnimation decay animation to be used to calculate closest point in the anchors set
- * considering velocity.
- */
-data class AnchorsFlingConfig(
-    val animationAnchors: List<Float>,
-    val onAnimationFinished: ((finishValue: Float, cancelled: Boolean) -> Unit)? = null,
-    val animationBuilder: AnimationBuilder<Float> = PhysicsBuilder(),
-    val decayAnimation: DecayAnimation = ExponentialDecay()
-) : FlingConfig {
-
-    private val adjust: (Float) -> TargetAnimation? = { target ->
-        val point = animationAnchors.minBy { abs(it - target) }
-        val adjusted = point ?: target
-        TargetAnimation(adjusted, animationBuilder)
-    }
-
-    override fun fling(
-        value: AnimatedFloat,
-        startVelocity: Float
-    ) {
-        value.fling(
-            startVelocity,
-            decayAnimation,
-            adjust,
-            onAnimationFinished?.let {
-                { cancelled: Boolean -> it.invoke(value.value, cancelled) }
-            }
-        )
-    }
-}
-
-/**
- * Config that provides natural fling with customizable decay behavior
- * e.g fling friction or velocity threshold. Natural fling config doesn't
- * specify where this fling will end.
- *
- * The most common Decay animation is [ExponentialDecay].
- *
- * @param decayAnimation the animation to control fling behaviour
- * @param onFlingFinished callback to be invoked when fling finishes by decay
- * or being interrupted by gesture input.
- * Consider second boolean param "cancelled" to know what happened.
- * @param adjustTarget callback to be called at the start of fling
- * so the final value for fling can be adjusted
- */
-data class DecayFlingConfig(
-    val decayAnimation: DecayAnimation,
-    val onFlingFinished: ((finishValue: Float, cancelled: Boolean) -> Unit)? = null,
-    val adjustTarget: (Float) -> TargetAnimation? = { null }
-) : FlingConfig {
-    override fun fling(value: AnimatedFloat, startVelocity: Float) {
-        value.fling(
-            startVelocity,
-            decayAnimation,
-            adjustTarget = adjustTarget,
-            onFinished = onFlingFinished?.let {
-                { cancelled: Boolean -> it.invoke(value.value, cancelled) }
-            }
-        )
-    }
-}
-
-/**
- * Default fling config sets decay animation to [ExponentialDecay] to provide natural fling
- * and no calls no callback when fling finishes.
- */
-object DefaultFlingConfig : FlingConfig {
-    override fun fling(value: AnimatedFloat, startVelocity: Float) {
-        value.fling(startVelocity, ExponentialDecay())
-    }
-}
\ No newline at end of file
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/MutuallyExclusiveSetItem.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/MutuallyExclusiveSetItem.kt
index af44074..a199158 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/MutuallyExclusiveSetItem.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/MutuallyExclusiveSetItem.kt
@@ -37,7 +37,7 @@
 fun MutuallyExclusiveSetItem(
     selected: Boolean,
     onClick: () -> Unit,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     // TODO: when semantics can be merged, we should make this use Clickable internally rather
     // than duplicating logic
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Toggleable.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Toggleable.kt
index 6531828..a9f6f7b 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Toggleable.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Toggleable.kt
@@ -28,7 +28,7 @@
 fun Toggleable(
     value: ToggleableState = ToggleableState.Checked,
     onToggle: (() -> Unit)? = null,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val actions = if (onToggle != null) {
         listOf(SemanticsAction(SemanticsActionType.Tap, onToggle))
diff --git a/ui/ui-framework/api/1.0.0-alpha01.txt b/ui/ui-framework/api/1.0.0-alpha01.txt
index c7820fa..0a535b2 100644
--- a/ui/ui-framework/api/1.0.0-alpha01.txt
+++ b/ui/ui-framework/api/1.0.0-alpha01.txt
@@ -32,11 +32,6 @@
     method public androidx.ui.text.TextStyle? getTextStyle();
   }
 
-  public final class InputFieldKt {
-    ctor public InputFieldKt();
-    method public static void InputField(androidx.ui.input.EditorState value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorState,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
-  }
-
   public final class IntrinsicMeasurementsReceiver implements androidx.ui.core.DensityReceiver {
     method public androidx.ui.core.Density getDensity();
     method public androidx.ui.core.IntPx maxIntrinsicHeight(androidx.ui.core.Measurable, androidx.ui.core.IntPx w);
@@ -151,6 +146,11 @@
     method public static androidx.compose.Ambient<java.lang.String> getTestTagAmbient();
   }
 
+  public final class TextFieldKt {
+    ctor public TextFieldKt();
+    method public static void TextField(androidx.ui.input.EditorModel value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorModel,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
+  }
+
   public final class TextKt {
     ctor public TextKt();
     method public static void CurrentTextStyleProvider(androidx.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -449,28 +449,91 @@
     method public void applyBrush(androidx.ui.painting.Paint p);
   }
 
+  public final class VectorAsset {
+    method public androidx.ui.core.Px getDefaultHeight();
+    method public androidx.ui.core.Px getDefaultWidth();
+    method public String getName();
+    method public androidx.ui.core.vectorgraphics.VectorGroup getRoot();
+    method public float getViewportHeight();
+    method public float getViewportWidth();
+  }
+
+  public final class VectorAssetBuilder {
+    ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+    method public androidx.ui.core.vectorgraphics.VectorAssetBuilder addPath(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
+    method public androidx.ui.core.vectorgraphics.VectorAsset build();
+    method public void ensureNotConsumed();
+    method public androidx.ui.core.Px getDefaultHeight();
+    method public androidx.ui.core.Px getDefaultWidth();
+    method public String getName();
+    method public float getViewportHeight();
+    method public float getViewportWidth();
+    method public androidx.ui.core.vectorgraphics.VectorAssetBuilder popGroup();
+    method public androidx.ui.core.vectorgraphics.VectorAssetBuilder pushGroup(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath);
+  }
+
+  public final class VectorAssetKt {
+    ctor public VectorAssetKt();
+    method public static void DrawVector(androidx.ui.core.vectorgraphics.VectorAsset vectorImage);
+  }
+
+  public final class VectorGroup extends androidx.ui.core.vectorgraphics.VectorNode implements java.lang.Iterable<androidx.ui.core.vectorgraphics.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
+    ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, Object? clipPathData);
+    ctor public VectorGroup();
+    method public operator androidx.ui.core.vectorgraphics.VectorNode get(int index);
+    method public Object? getClipPathData();
+    method public String getName();
+    method public float getPivotX();
+    method public float getPivotY();
+    method public float getRotation();
+    method public float getScaleX();
+    method public float getScaleY();
+    method public int getSize();
+    method public float getTranslationX();
+    method public float getTranslationY();
+    method public java.util.Iterator<androidx.ui.core.vectorgraphics.VectorNode> iterator();
+    property public final int size;
+  }
+
   public final class VectorKt {
     ctor public VectorKt();
+    method public static void Group(String name = "", float rotation = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function0<kotlin.Unit> childNodes);
+    method public static void Path(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
+    method public static void Vector(String name = "", float viewportWidth, float viewportHeight, androidx.ui.core.Px defaultWidth = Px(viewportWidth), androidx.ui.core.Px defaultHeight = Px(viewportHeight), kotlin.jvm.functions.Function0<kotlin.Unit> children);
     method public static androidx.ui.core.vectorgraphics.PathNode![] addPathNodes(String? pathStr);
     method public static android.view.View? adoptVectorGraphic(Object? parent, Object? child);
     method public static androidx.ui.painting.StrokeCap getDefaultStrokeLineCap();
     method public static androidx.ui.painting.StrokeJoin getDefaultStrokeLineJoin();
     method public static androidx.ui.core.vectorgraphics.PathNode![] getEmptyPath();
-    method public static void group(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translateX = 0.0f, float translateY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function0<kotlin.Unit> childNodes);
-    method public static void path(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
-    method public static void vector(String name = "", float viewportWidth, float viewportHeight, float defaultWidth = viewportWidth, float defaultHeight = viewportHeight, kotlin.jvm.functions.Function0<kotlin.Unit> children);
     field public static final float DefaultAlpha = 1.0f;
     field public static final String DefaultGroupName = "";
     field public static final String DefaultPathName = "";
     field public static final float DefaultPivotX = 0.0f;
     field public static final float DefaultPivotY = 0.0f;
-    field public static final float DefaultRotate = 0.0f;
+    field public static final float DefaultRotation = 0.0f;
     field public static final float DefaultScaleX = 1.0f;
     field public static final float DefaultScaleY = 1.0f;
     field public static final float DefaultStrokeLineMiter = 4.0f;
     field public static final float DefaultStrokeLineWidth = 0.0f;
-    field public static final float DefaultTranslateX = 0.0f;
-    field public static final float DefaultTranslateY = 0.0f;
+    field public static final float DefaultTranslationX = 0.0f;
+    field public static final float DefaultTranslationY = 0.0f;
+  }
+
+  public abstract sealed class VectorNode {
+  }
+
+  public final class VectorPath extends androidx.ui.core.vectorgraphics.VectorNode {
+    ctor public VectorPath(String name, Object? pathData, Object fill, float fillAlpha, Object stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.painting.StrokeCap strokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin, float strokeLineMiter);
+    method public Object getFill();
+    method public float getFillAlpha();
+    method public String getName();
+    method public Object? getPathData();
+    method public Object getStroke();
+    method public float getStrokeAlpha();
+    method public androidx.ui.painting.StrokeCap getStrokeLineCap();
+    method public androidx.ui.painting.StrokeJoin getStrokeLineJoin();
+    method public float getStrokeLineMiter();
+    method public float getStrokeLineWidth();
   }
 
 }
@@ -479,7 +542,12 @@
 
   public final class VectorResourceKt {
     ctor public VectorResourceKt();
-    method public static void vectorResource(android.content.res.Resources res, int resId);
+    method public static void VectorResource(int resId);
+    method public static androidx.ui.core.vectorgraphics.VectorAsset loadVectorResource(android.content.res.Resources.Theme? theme = null, android.content.res.Resources res, int resId) throws org.xmlpull.v1.XmlPullParserException;
+  }
+
+  public final class XmlVectorParserKt {
+    ctor public XmlVectorParserKt();
   }
 
 }
diff --git a/ui/ui-framework/api/current.txt b/ui/ui-framework/api/current.txt
index c7820fa..0a535b2 100644
--- a/ui/ui-framework/api/current.txt
+++ b/ui/ui-framework/api/current.txt
@@ -32,11 +32,6 @@
     method public androidx.ui.text.TextStyle? getTextStyle();
   }
 
-  public final class InputFieldKt {
-    ctor public InputFieldKt();
-    method public static void InputField(androidx.ui.input.EditorState value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorState,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
-  }
-
   public final class IntrinsicMeasurementsReceiver implements androidx.ui.core.DensityReceiver {
     method public androidx.ui.core.Density getDensity();
     method public androidx.ui.core.IntPx maxIntrinsicHeight(androidx.ui.core.Measurable, androidx.ui.core.IntPx w);
@@ -151,6 +146,11 @@
     method public static androidx.compose.Ambient<java.lang.String> getTestTagAmbient();
   }
 
+  public final class TextFieldKt {
+    ctor public TextFieldKt();
+    method public static void TextField(androidx.ui.input.EditorModel value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorModel,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
+  }
+
   public final class TextKt {
     ctor public TextKt();
     method public static void CurrentTextStyleProvider(androidx.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -449,28 +449,91 @@
     method public void applyBrush(androidx.ui.painting.Paint p);
   }
 
+  public final class VectorAsset {
+    method public androidx.ui.core.Px getDefaultHeight();
+    method public androidx.ui.core.Px getDefaultWidth();
+    method public String getName();
+    method public androidx.ui.core.vectorgraphics.VectorGroup getRoot();
+    method public float getViewportHeight();
+    method public float getViewportWidth();
+  }
+
+  public final class VectorAssetBuilder {
+    ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+    method public androidx.ui.core.vectorgraphics.VectorAssetBuilder addPath(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
+    method public androidx.ui.core.vectorgraphics.VectorAsset build();
+    method public void ensureNotConsumed();
+    method public androidx.ui.core.Px getDefaultHeight();
+    method public androidx.ui.core.Px getDefaultWidth();
+    method public String getName();
+    method public float getViewportHeight();
+    method public float getViewportWidth();
+    method public androidx.ui.core.vectorgraphics.VectorAssetBuilder popGroup();
+    method public androidx.ui.core.vectorgraphics.VectorAssetBuilder pushGroup(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath);
+  }
+
+  public final class VectorAssetKt {
+    ctor public VectorAssetKt();
+    method public static void DrawVector(androidx.ui.core.vectorgraphics.VectorAsset vectorImage);
+  }
+
+  public final class VectorGroup extends androidx.ui.core.vectorgraphics.VectorNode implements java.lang.Iterable<androidx.ui.core.vectorgraphics.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
+    ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, Object? clipPathData);
+    ctor public VectorGroup();
+    method public operator androidx.ui.core.vectorgraphics.VectorNode get(int index);
+    method public Object? getClipPathData();
+    method public String getName();
+    method public float getPivotX();
+    method public float getPivotY();
+    method public float getRotation();
+    method public float getScaleX();
+    method public float getScaleY();
+    method public int getSize();
+    method public float getTranslationX();
+    method public float getTranslationY();
+    method public java.util.Iterator<androidx.ui.core.vectorgraphics.VectorNode> iterator();
+    property public final int size;
+  }
+
   public final class VectorKt {
     ctor public VectorKt();
+    method public static void Group(String name = "", float rotation = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function0<kotlin.Unit> childNodes);
+    method public static void Path(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
+    method public static void Vector(String name = "", float viewportWidth, float viewportHeight, androidx.ui.core.Px defaultWidth = Px(viewportWidth), androidx.ui.core.Px defaultHeight = Px(viewportHeight), kotlin.jvm.functions.Function0<kotlin.Unit> children);
     method public static androidx.ui.core.vectorgraphics.PathNode![] addPathNodes(String? pathStr);
     method public static android.view.View? adoptVectorGraphic(Object? parent, Object? child);
     method public static androidx.ui.painting.StrokeCap getDefaultStrokeLineCap();
     method public static androidx.ui.painting.StrokeJoin getDefaultStrokeLineJoin();
     method public static androidx.ui.core.vectorgraphics.PathNode![] getEmptyPath();
-    method public static void group(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translateX = 0.0f, float translateY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function0<kotlin.Unit> childNodes);
-    method public static void path(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
-    method public static void vector(String name = "", float viewportWidth, float viewportHeight, float defaultWidth = viewportWidth, float defaultHeight = viewportHeight, kotlin.jvm.functions.Function0<kotlin.Unit> children);
     field public static final float DefaultAlpha = 1.0f;
     field public static final String DefaultGroupName = "";
     field public static final String DefaultPathName = "";
     field public static final float DefaultPivotX = 0.0f;
     field public static final float DefaultPivotY = 0.0f;
-    field public static final float DefaultRotate = 0.0f;
+    field public static final float DefaultRotation = 0.0f;
     field public static final float DefaultScaleX = 1.0f;
     field public static final float DefaultScaleY = 1.0f;
     field public static final float DefaultStrokeLineMiter = 4.0f;
     field public static final float DefaultStrokeLineWidth = 0.0f;
-    field public static final float DefaultTranslateX = 0.0f;
-    field public static final float DefaultTranslateY = 0.0f;
+    field public static final float DefaultTranslationX = 0.0f;
+    field public static final float DefaultTranslationY = 0.0f;
+  }
+
+  public abstract sealed class VectorNode {
+  }
+
+  public final class VectorPath extends androidx.ui.core.vectorgraphics.VectorNode {
+    ctor public VectorPath(String name, Object? pathData, Object fill, float fillAlpha, Object stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.painting.StrokeCap strokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin, float strokeLineMiter);
+    method public Object getFill();
+    method public float getFillAlpha();
+    method public String getName();
+    method public Object? getPathData();
+    method public Object getStroke();
+    method public float getStrokeAlpha();
+    method public androidx.ui.painting.StrokeCap getStrokeLineCap();
+    method public androidx.ui.painting.StrokeJoin getStrokeLineJoin();
+    method public float getStrokeLineMiter();
+    method public float getStrokeLineWidth();
   }
 
 }
@@ -479,7 +542,12 @@
 
   public final class VectorResourceKt {
     ctor public VectorResourceKt();
-    method public static void vectorResource(android.content.res.Resources res, int resId);
+    method public static void VectorResource(int resId);
+    method public static androidx.ui.core.vectorgraphics.VectorAsset loadVectorResource(android.content.res.Resources.Theme? theme = null, android.content.res.Resources res, int resId) throws org.xmlpull.v1.XmlPullParserException;
+  }
+
+  public final class XmlVectorParserKt {
+    ctor public XmlVectorParserKt();
   }
 
 }
diff --git a/ui/ui-framework/api/restricted_1.0.0-alpha01.txt b/ui/ui-framework/api/restricted_1.0.0-alpha01.txt
index c7820fa..0a535b2 100644
--- a/ui/ui-framework/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-framework/api/restricted_1.0.0-alpha01.txt
@@ -32,11 +32,6 @@
     method public androidx.ui.text.TextStyle? getTextStyle();
   }
 
-  public final class InputFieldKt {
-    ctor public InputFieldKt();
-    method public static void InputField(androidx.ui.input.EditorState value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorState,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
-  }
-
   public final class IntrinsicMeasurementsReceiver implements androidx.ui.core.DensityReceiver {
     method public androidx.ui.core.Density getDensity();
     method public androidx.ui.core.IntPx maxIntrinsicHeight(androidx.ui.core.Measurable, androidx.ui.core.IntPx w);
@@ -151,6 +146,11 @@
     method public static androidx.compose.Ambient<java.lang.String> getTestTagAmbient();
   }
 
+  public final class TextFieldKt {
+    ctor public TextFieldKt();
+    method public static void TextField(androidx.ui.input.EditorModel value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorModel,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
+  }
+
   public final class TextKt {
     ctor public TextKt();
     method public static void CurrentTextStyleProvider(androidx.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -449,28 +449,91 @@
     method public void applyBrush(androidx.ui.painting.Paint p);
   }
 
+  public final class VectorAsset {
+    method public androidx.ui.core.Px getDefaultHeight();
+    method public androidx.ui.core.Px getDefaultWidth();
+    method public String getName();
+    method public androidx.ui.core.vectorgraphics.VectorGroup getRoot();
+    method public float getViewportHeight();
+    method public float getViewportWidth();
+  }
+
+  public final class VectorAssetBuilder {
+    ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+    method public androidx.ui.core.vectorgraphics.VectorAssetBuilder addPath(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
+    method public androidx.ui.core.vectorgraphics.VectorAsset build();
+    method public void ensureNotConsumed();
+    method public androidx.ui.core.Px getDefaultHeight();
+    method public androidx.ui.core.Px getDefaultWidth();
+    method public String getName();
+    method public float getViewportHeight();
+    method public float getViewportWidth();
+    method public androidx.ui.core.vectorgraphics.VectorAssetBuilder popGroup();
+    method public androidx.ui.core.vectorgraphics.VectorAssetBuilder pushGroup(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath);
+  }
+
+  public final class VectorAssetKt {
+    ctor public VectorAssetKt();
+    method public static void DrawVector(androidx.ui.core.vectorgraphics.VectorAsset vectorImage);
+  }
+
+  public final class VectorGroup extends androidx.ui.core.vectorgraphics.VectorNode implements java.lang.Iterable<androidx.ui.core.vectorgraphics.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
+    ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, Object? clipPathData);
+    ctor public VectorGroup();
+    method public operator androidx.ui.core.vectorgraphics.VectorNode get(int index);
+    method public Object? getClipPathData();
+    method public String getName();
+    method public float getPivotX();
+    method public float getPivotY();
+    method public float getRotation();
+    method public float getScaleX();
+    method public float getScaleY();
+    method public int getSize();
+    method public float getTranslationX();
+    method public float getTranslationY();
+    method public java.util.Iterator<androidx.ui.core.vectorgraphics.VectorNode> iterator();
+    property public final int size;
+  }
+
   public final class VectorKt {
     ctor public VectorKt();
+    method public static void Group(String name = "", float rotation = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function0<kotlin.Unit> childNodes);
+    method public static void Path(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
+    method public static void Vector(String name = "", float viewportWidth, float viewportHeight, androidx.ui.core.Px defaultWidth = Px(viewportWidth), androidx.ui.core.Px defaultHeight = Px(viewportHeight), kotlin.jvm.functions.Function0<kotlin.Unit> children);
     method public static androidx.ui.core.vectorgraphics.PathNode![] addPathNodes(String? pathStr);
     method public static android.view.View? adoptVectorGraphic(Object? parent, Object? child);
     method public static androidx.ui.painting.StrokeCap getDefaultStrokeLineCap();
     method public static androidx.ui.painting.StrokeJoin getDefaultStrokeLineJoin();
     method public static androidx.ui.core.vectorgraphics.PathNode![] getEmptyPath();
-    method public static void group(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translateX = 0.0f, float translateY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function0<kotlin.Unit> childNodes);
-    method public static void path(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
-    method public static void vector(String name = "", float viewportWidth, float viewportHeight, float defaultWidth = viewportWidth, float defaultHeight = viewportHeight, kotlin.jvm.functions.Function0<kotlin.Unit> children);
     field public static final float DefaultAlpha = 1.0f;
     field public static final String DefaultGroupName = "";
     field public static final String DefaultPathName = "";
     field public static final float DefaultPivotX = 0.0f;
     field public static final float DefaultPivotY = 0.0f;
-    field public static final float DefaultRotate = 0.0f;
+    field public static final float DefaultRotation = 0.0f;
     field public static final float DefaultScaleX = 1.0f;
     field public static final float DefaultScaleY = 1.0f;
     field public static final float DefaultStrokeLineMiter = 4.0f;
     field public static final float DefaultStrokeLineWidth = 0.0f;
-    field public static final float DefaultTranslateX = 0.0f;
-    field public static final float DefaultTranslateY = 0.0f;
+    field public static final float DefaultTranslationX = 0.0f;
+    field public static final float DefaultTranslationY = 0.0f;
+  }
+
+  public abstract sealed class VectorNode {
+  }
+
+  public final class VectorPath extends androidx.ui.core.vectorgraphics.VectorNode {
+    ctor public VectorPath(String name, Object? pathData, Object fill, float fillAlpha, Object stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.painting.StrokeCap strokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin, float strokeLineMiter);
+    method public Object getFill();
+    method public float getFillAlpha();
+    method public String getName();
+    method public Object? getPathData();
+    method public Object getStroke();
+    method public float getStrokeAlpha();
+    method public androidx.ui.painting.StrokeCap getStrokeLineCap();
+    method public androidx.ui.painting.StrokeJoin getStrokeLineJoin();
+    method public float getStrokeLineMiter();
+    method public float getStrokeLineWidth();
   }
 
 }
@@ -479,7 +542,12 @@
 
   public final class VectorResourceKt {
     ctor public VectorResourceKt();
-    method public static void vectorResource(android.content.res.Resources res, int resId);
+    method public static void VectorResource(int resId);
+    method public static androidx.ui.core.vectorgraphics.VectorAsset loadVectorResource(android.content.res.Resources.Theme? theme = null, android.content.res.Resources res, int resId) throws org.xmlpull.v1.XmlPullParserException;
+  }
+
+  public final class XmlVectorParserKt {
+    ctor public XmlVectorParserKt();
   }
 
 }
diff --git a/ui/ui-framework/api/restricted_current.txt b/ui/ui-framework/api/restricted_current.txt
index c7820fa..0a535b2 100644
--- a/ui/ui-framework/api/restricted_current.txt
+++ b/ui/ui-framework/api/restricted_current.txt
@@ -32,11 +32,6 @@
     method public androidx.ui.text.TextStyle? getTextStyle();
   }
 
-  public final class InputFieldKt {
-    ctor public InputFieldKt();
-    method public static void InputField(androidx.ui.input.EditorState value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorState,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
-  }
-
   public final class IntrinsicMeasurementsReceiver implements androidx.ui.core.DensityReceiver {
     method public androidx.ui.core.Density getDensity();
     method public androidx.ui.core.IntPx maxIntrinsicHeight(androidx.ui.core.Measurable, androidx.ui.core.IntPx w);
@@ -151,6 +146,11 @@
     method public static androidx.compose.Ambient<java.lang.String> getTestTagAmbient();
   }
 
+  public final class TextFieldKt {
+    ctor public TextFieldKt();
+    method public static void TextField(androidx.ui.input.EditorModel value, androidx.ui.core.EditorStyle editorStyle, androidx.ui.input.KeyboardType keyboardType = KeyboardType.Text, androidx.ui.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function1<? super androidx.ui.input.EditorModel,kotlin.Unit> onValueChange = {}, kotlin.jvm.functions.Function1<? super androidx.ui.input.ImeAction,kotlin.Unit> onImeActionPerformed = {}, androidx.ui.core.VisualTransformation? visualTransformation = null);
+  }
+
   public final class TextKt {
     ctor public TextKt();
     method public static void CurrentTextStyleProvider(androidx.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -449,28 +449,91 @@
     method public void applyBrush(androidx.ui.painting.Paint p);
   }
 
+  public final class VectorAsset {
+    method public androidx.ui.core.Px getDefaultHeight();
+    method public androidx.ui.core.Px getDefaultWidth();
+    method public String getName();
+    method public androidx.ui.core.vectorgraphics.VectorGroup getRoot();
+    method public float getViewportHeight();
+    method public float getViewportWidth();
+  }
+
+  public final class VectorAssetBuilder {
+    ctor public VectorAssetBuilder(String name, androidx.ui.core.Px defaultWidth, androidx.ui.core.Px defaultHeight, float viewportWidth, float viewportHeight);
+    method public androidx.ui.core.vectorgraphics.VectorAssetBuilder addPath(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
+    method public androidx.ui.core.vectorgraphics.VectorAsset build();
+    method public void ensureNotConsumed();
+    method public androidx.ui.core.Px getDefaultHeight();
+    method public androidx.ui.core.Px getDefaultWidth();
+    method public String getName();
+    method public float getViewportHeight();
+    method public float getViewportWidth();
+    method public androidx.ui.core.vectorgraphics.VectorAssetBuilder popGroup();
+    method public androidx.ui.core.vectorgraphics.VectorAssetBuilder pushGroup(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath);
+  }
+
+  public final class VectorAssetKt {
+    ctor public VectorAssetKt();
+    method public static void DrawVector(androidx.ui.core.vectorgraphics.VectorAsset vectorImage);
+  }
+
+  public final class VectorGroup extends androidx.ui.core.vectorgraphics.VectorNode implements java.lang.Iterable<androidx.ui.core.vectorgraphics.VectorNode> kotlin.jvm.internal.markers.KMappedMarker {
+    ctor public VectorGroup(String name, float rotation, float pivotX, float pivotY, float scaleX, float scaleY, float translationX, float translationY, Object? clipPathData);
+    ctor public VectorGroup();
+    method public operator androidx.ui.core.vectorgraphics.VectorNode get(int index);
+    method public Object? getClipPathData();
+    method public String getName();
+    method public float getPivotX();
+    method public float getPivotY();
+    method public float getRotation();
+    method public float getScaleX();
+    method public float getScaleY();
+    method public int getSize();
+    method public float getTranslationX();
+    method public float getTranslationY();
+    method public java.util.Iterator<androidx.ui.core.vectorgraphics.VectorNode> iterator();
+    property public final int size;
+  }
+
   public final class VectorKt {
     ctor public VectorKt();
+    method public static void Group(String name = "", float rotation = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translationX = 0.0f, float translationY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function0<kotlin.Unit> childNodes);
+    method public static void Path(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
+    method public static void Vector(String name = "", float viewportWidth, float viewportHeight, androidx.ui.core.Px defaultWidth = Px(viewportWidth), androidx.ui.core.Px defaultHeight = Px(viewportHeight), kotlin.jvm.functions.Function0<kotlin.Unit> children);
     method public static androidx.ui.core.vectorgraphics.PathNode![] addPathNodes(String? pathStr);
     method public static android.view.View? adoptVectorGraphic(Object? parent, Object? child);
     method public static androidx.ui.painting.StrokeCap getDefaultStrokeLineCap();
     method public static androidx.ui.painting.StrokeJoin getDefaultStrokeLineJoin();
     method public static androidx.ui.core.vectorgraphics.PathNode![] getEmptyPath();
-    method public static void group(String name = "", float rotate = 0.0f, float pivotX = 0.0f, float pivotY = 0.0f, float scaleX = 1.0f, float scaleY = 1.0f, float translateX = 0.0f, float translateY = 0.0f, Object? clipPathData = EmptyPath, kotlin.jvm.functions.Function0<kotlin.Unit> childNodes);
-    method public static void path(Object? pathData, String name = "", Object fill = EmptyBrush, float fillAlpha = 1.0f, Object stroke = EmptyBrush, float strokeAlpha = 1.0f, float strokeLineWidth = 0.0f, androidx.ui.painting.StrokeCap strokeLineCap = DefaultStrokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin = DefaultStrokeLineJoin, float strokeLineMiter = 4.0f);
-    method public static void vector(String name = "", float viewportWidth, float viewportHeight, float defaultWidth = viewportWidth, float defaultHeight = viewportHeight, kotlin.jvm.functions.Function0<kotlin.Unit> children);
     field public static final float DefaultAlpha = 1.0f;
     field public static final String DefaultGroupName = "";
     field public static final String DefaultPathName = "";
     field public static final float DefaultPivotX = 0.0f;
     field public static final float DefaultPivotY = 0.0f;
-    field public static final float DefaultRotate = 0.0f;
+    field public static final float DefaultRotation = 0.0f;
     field public static final float DefaultScaleX = 1.0f;
     field public static final float DefaultScaleY = 1.0f;
     field public static final float DefaultStrokeLineMiter = 4.0f;
     field public static final float DefaultStrokeLineWidth = 0.0f;
-    field public static final float DefaultTranslateX = 0.0f;
-    field public static final float DefaultTranslateY = 0.0f;
+    field public static final float DefaultTranslationX = 0.0f;
+    field public static final float DefaultTranslationY = 0.0f;
+  }
+
+  public abstract sealed class VectorNode {
+  }
+
+  public final class VectorPath extends androidx.ui.core.vectorgraphics.VectorNode {
+    ctor public VectorPath(String name, Object? pathData, Object fill, float fillAlpha, Object stroke, float strokeAlpha, float strokeLineWidth, androidx.ui.painting.StrokeCap strokeLineCap, androidx.ui.painting.StrokeJoin strokeLineJoin, float strokeLineMiter);
+    method public Object getFill();
+    method public float getFillAlpha();
+    method public String getName();
+    method public Object? getPathData();
+    method public Object getStroke();
+    method public float getStrokeAlpha();
+    method public androidx.ui.painting.StrokeCap getStrokeLineCap();
+    method public androidx.ui.painting.StrokeJoin getStrokeLineJoin();
+    method public float getStrokeLineMiter();
+    method public float getStrokeLineWidth();
   }
 
 }
@@ -479,7 +542,12 @@
 
   public final class VectorResourceKt {
     ctor public VectorResourceKt();
-    method public static void vectorResource(android.content.res.Resources res, int resId);
+    method public static void VectorResource(int resId);
+    method public static androidx.ui.core.vectorgraphics.VectorAsset loadVectorResource(android.content.res.Resources.Theme? theme = null, android.content.res.Resources res, int resId) throws org.xmlpull.v1.XmlPullParserException;
+  }
+
+  public final class XmlVectorParserKt {
+    ctor public XmlVectorParserKt();
   }
 
 }
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
index 443fcd6..2ca3de7 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
@@ -63,7 +63,7 @@
 fun HeaderFooterLayout(
     header: @Composable() () -> Unit,
     footer: @Composable() () -> Unit,
-    @Children content: @Composable() () -> Unit
+    content: @Composable() () -> Unit
 ) {
     @Suppress("USELESS_CAST")
     Layout(
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt
index 74904e6..a24fa7b 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL1.kt
@@ -144,7 +144,7 @@
     onPress: SemanticAction<PxPosition> = SemanticAction(defaultParam = PxPosition.Origin) { },
     onRelease: SemanticAction<Unit> = SemanticAction(defaultParam = Unit) { },
     onCancel: SemanticAction<Unit> = SemanticAction(defaultParam = Unit) { },
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     PressGestureDetector(
         onPress = { onPress.action(ActionParam(ActionCaller.PointerInput, it)) },
@@ -165,7 +165,7 @@
 fun Semantics(
     @Suppress("UNUSED_PARAMETER") properties: Set<SemanticProperty<out Any>> = setOf(),
     actions: Set<SemanticAction<out Any?>> = setOf(),
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     Column {
         MaterialTheme {
@@ -275,7 +275,7 @@
  * children or make them visible.
  */
 @Composable
-private fun Collapsable(@Children children: @Composable() () -> Unit) {
+private fun Collapsable(children: @Composable() () -> Unit) {
 
     val collapsedState = +state { CollapseMode.Collapsed }
 
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL2.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL2.kt
index a1aa21f..031632f 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL2.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL2.kt
@@ -28,7 +28,7 @@
     label: String = "",
     visibility: Visibility = Visibility.Undefined,
     actions: Set<SemanticAction<out Any?>> = setOf(),
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val propertySet = mutableSetOf<SemanticProperty<out Any>>()
 
@@ -62,7 +62,7 @@
     defaultParam: T,
     types: Set<ActionType> = setOf(),
     action: (ActionParam<T>) -> Unit,
-    @Children block: @Composable() (SemanticAction<T>) -> Unit
+    block: @Composable() (SemanticAction<T>) -> Unit
 ) {
     val semanticAction = SemanticAction<T>(phrase, defaultParam, types, action)
     block.invoke(semanticAction)
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL3.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL3.kt
index 9b5eed4..565ab99 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL3.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/SemanticsL3.kt
@@ -29,7 +29,7 @@
 @Composable
 fun ClickInteraction(
     click: SemanticActionBuilder<Unit>.() -> Unit,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val clickAction = SemanticActionBuilder(phrase = "Click", defaultParam = Unit)
         .apply(click)
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/VectorGraphicsActivity.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/VectorGraphicsActivity.kt
index e6288ff..1153956 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/VectorGraphicsActivity.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/VectorGraphicsActivity.kt
@@ -17,38 +17,39 @@
 package androidx.ui.framework.demos
 
 import android.app.Activity
-import android.graphics.Color
 import android.os.Bundle
 import android.widget.LinearLayout
 import androidx.ui.core.vectorgraphics.adoptVectorGraphic
-import androidx.ui.core.vectorgraphics.compat.vectorResource
-import androidx.ui.core.vectorgraphics.group
-import androidx.ui.core.vectorgraphics.path
-import androidx.ui.core.vectorgraphics.vector
+import androidx.ui.core.vectorgraphics.Group
+import androidx.ui.core.vectorgraphics.Path
+import androidx.ui.core.vectorgraphics.Vector
 import androidx.ui.core.vectorgraphics.PathBuilder
 import androidx.ui.core.vectorgraphics.PathDelegate
 import androidx.compose.Composable
 import androidx.compose.composer
 import androidx.compose.registerAdapter
 import androidx.compose.setViewContent
+import androidx.ui.core.ContextAmbient
+import androidx.ui.core.Px
+import androidx.ui.core.vectorgraphics.compat.VectorResource
+import androidx.ui.graphics.Color
 
 class VectorGraphicsActivity : Activity() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
-        val res = getResources()
         setViewContent {
             composer.registerAdapter { parent, child ->
                 adoptVectorGraphic(parent, child)
             }
 
             LinearLayout(orientation = LinearLayout.VERTICAL) {
-                vectorResource(
-                    res = res,
-                    resId = androidx.ui.framework.demos.R.drawable.ic_crane
-                )
-                vectorShape()
+                // TODO Make composition of components with Android Views automatically wire the appropriate ambients
+                ContextAmbient.Provider(value = this@VectorGraphicsActivity) {
+                    VectorResource(resId = androidx.ui.framework.demos.R.drawable.ic_crane)
+                    vectorShape()
+                }
             }
         }
     }
@@ -57,28 +58,28 @@
     fun vectorShape() {
         val viewportWidth = 300.0f
         val viewportHeight = 300.0f
-        vector(
+        Vector(
             name = "vectorShape",
-            defaultWidth = 300.0f,
-            defaultHeight = 300.0f,
+            defaultWidth = Px(300.0f),
+            defaultHeight = Px(300.0f),
             viewportWidth = viewportWidth,
             viewportHeight = viewportHeight
         ) {
-            group(
+            Group(
                 scaleX = 0.75f,
                 scaleY = 0.75f,
-                rotate = 45.0f,
+                rotation = 45.0f,
                 pivotX = (viewportWidth / 2),
                 pivotY = (viewportHeight / 2)
             ) {
                 backgroundPath(vectorWidth = viewportWidth, vectorHeight = viewportHeight)
                 stripePath(vectorWidth = viewportWidth, vectorHeight = viewportHeight)
-                group(
-                    translateX = 50.0f,
-                    translateY = 50.0f,
+                Group(
+                    translationX = 50.0f,
+                    translationY = 50.0f,
                     pivotX = (viewportWidth / 2),
                     pivotY = (viewportHeight / 2),
-                    rotate = 25.0f
+                    rotation = 25.0f
                 ) {
                     val pathData = PathDelegate {
                         moveTo(viewportWidth / 2 - 100, viewportHeight / 2 - 100)
@@ -87,7 +88,7 @@
                         horizontalLineToRelative(-200.0f)
                         close()
                     }
-                    path(fill = Color.MAGENTA, pathData = pathData)
+                    Path(fill = Color.Magenta, pathData = pathData)
                 }
             }
         }
@@ -102,7 +103,7 @@
             close()
         }
 
-        path(fill = Color.CYAN, pathData = background)
+        Path(fill = Color.Cyan, pathData = background)
     }
 
     @Composable
@@ -111,7 +112,7 @@
             stripe(vectorWidth, vectorHeight, 10)
         }
 
-        path(stroke = Color.BLUE, pathData = stripeDelegate)
+        Path(stroke = Color.Blue, pathData = stripeDelegate)
     }
 
     private fun PathBuilder.stripe(vectorWidth: Float, vectorHeight: Float, numLines: Int) {
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/NestedScrollingDemo.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/NestedScrollingDemo.kt
index dc3feee..4074e44 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/NestedScrollingDemo.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/NestedScrollingDemo.kt
@@ -80,7 +80,7 @@
  * A very simple ScrollView like implementation that allows for vertical scrolling.
  */
 @Composable
-private fun Draggable(@Children children: @Composable() () -> Unit) {
+private fun Draggable(children: @Composable() () -> Unit) {
     val offset = +state { 0.px }
     val maxOffset = +state { 0.px }
 
@@ -215,7 +215,7 @@
  * A simple composable that arranges it's children as vertical list of items.
  */
 @Composable
-private fun Column(@Children children: @Composable() () -> Unit) {
+private fun Column(children: @Composable() () -> Unit) {
     Layout(children) { measurables, constraints ->
         var height = 0.ipx
         val placeables = measurables.map {
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/SimpleComposables.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/SimpleComposables.kt
index e49a48e..dc07bc9 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/SimpleComposables.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/SimpleComposables.kt
@@ -35,7 +35,7 @@
  * A simple layout composable that matches the size of it's parent layout.
  */
 @Composable
-internal fun MatchParent(@Children children: @Composable() () -> Unit) {
+internal fun MatchParent(children: @Composable() () -> Unit) {
     Layout({
         children()
     }, { _, constraints ->
@@ -44,7 +44,7 @@
 }
 
 @Composable
-internal fun Center(@Children children: @Composable() () -> Unit) {
+internal fun Center(children: @Composable() () -> Unit) {
     Layout(children) { measurables, constraints ->
         val placeable = measurables.first().measure(constraints)
         layout(constraints.maxWidth, constraints.maxHeight) {
@@ -59,7 +59,7 @@
  * A simple composable that pads items by [padding].
  */
 @Composable
-private fun Padding(padding: Dp?, @Children children: @Composable() () -> Unit) {
+private fun Padding(padding: Dp?, children: @Composable() () -> Unit) {
     Layout(children) { measurables, constraints ->
         val paddingPx = padding?.toIntPx() ?: 0.ipx
         val doublePadding = paddingPx * 2
@@ -90,7 +90,7 @@
  * A simple composable that draws a border around it's children.
  */
 @Composable
-private fun Border(color: Color, width: Dp, @Children children: @Composable() () -> Unit) {
+private fun Border(color: Color, width: Dp, children: @Composable() () -> Unit) {
     Layout(
         children = {
             children()
@@ -140,7 +140,7 @@
     width: Dp,
     height: Dp,
     padding: Dp,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
 
     val borderWidth: Dp = 2.dp
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
index 2318041..3c79ab3 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
@@ -553,7 +553,7 @@
         fun FixedSizeRow(
             width: IntPx,
             height: IntPx,
-            @Children children: @Composable() () -> Unit
+            children: @Composable() () -> Unit
         ) {
             Layout(children = children, layoutBlock = { measurables, constraints ->
                 val resolvedWidth = width.coerceIn(constraints.minWidth, constraints.maxWidth)
@@ -936,7 +936,7 @@
 }
 
 @Composable
-fun AtLeastSize(size: IntPx, @Children children: @Composable() () -> Unit) {
+fun AtLeastSize(size: IntPx, children: @Composable() () -> Unit) {
     Layout(
         layoutBlock = { measurables, constraints ->
             val newConstraints = Constraints(
@@ -964,7 +964,7 @@
 }
 
 @Composable
-fun Align(@Children children: @Composable() () -> Unit) {
+fun Align(children: @Composable() () -> Unit) {
     Layout(
         layoutBlock = { measurables, constraints ->
             val newConstraints = Constraints(
@@ -992,7 +992,7 @@
 }
 
 @Composable
-fun Padding(size: IntPx, @Children children: @Composable() () -> Unit) {
+fun Padding(size: IntPx, children: @Composable() () -> Unit) {
     Layout(
         layoutBlock = { measurables, constraints ->
             val totalDiff = size * 2
@@ -1024,7 +1024,7 @@
 fun TwoMeasureLayout(
     size: IntPx,
     latch: CountDownLatch,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     Layout(children = children) { measurables, _ ->
         val testConstraints = Constraints()
@@ -1046,7 +1046,7 @@
 }
 
 @Composable
-fun Position(size: IntPx, offset: OffsetModel, @Children children: @Composable() () -> Unit) {
+fun Position(size: IntPx, offset: OffsetModel, children: @Composable() () -> Unit) {
     Layout(children) { measurables, constraints ->
         val placeables = measurables.map { m ->
             m.measure(constraints)
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/TextLayoutTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/TextLayoutTest.kt
index e31200b..9f42391 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/TextLayoutTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/TextLayoutTest.kt
@@ -163,7 +163,7 @@
         assertTrue(intrinsicsLatch.await(1, TimeUnit.SECONDS))
     }
 
-    private fun show(@Children composable: @Composable() () -> Unit) {
+    private fun show(composable: @Composable() () -> Unit) {
         val runnable: Runnable = object : Runnable {
             override fun run() {
                 activity.setContent {
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/vectorgraphics/compat/XmlVectorParserTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/vectorgraphics/compat/XmlVectorParserTest.kt
new file mode 100644
index 0000000..6c5ff53
--- /dev/null
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/vectorgraphics/compat/XmlVectorParserTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.core.vectorgraphics.compat
+
+import android.util.Log
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.ui.core.Px
+import androidx.ui.core.vectorgraphics.PathCommand
+import androidx.ui.core.vectorgraphics.PathNode
+import androidx.ui.core.vectorgraphics.VectorPath
+import androidx.ui.framework.test.R
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class XmlVectorParserTest {
+
+    @Test
+    fun testParseXml() {
+        val res = InstrumentationRegistry.getInstrumentation().targetContext.resources
+        val asset = loadVectorResource(null, res, R.drawable.test_compose_vector)
+        val density = res.displayMetrics.density
+        val expectedSize = Px(density*24)
+        assertEquals(expectedSize, asset.defaultWidth)
+        assertEquals(expectedSize, asset.defaultHeight)
+        assertEquals(24.0f, asset.viewportWidth)
+        assertEquals(24.0f, asset.viewportHeight)
+        assertEquals(1, asset.root.size)
+
+        val node = asset.root.iterator().next() as VectorPath
+        assertEquals(0xFFFF0000.toInt(), node.fill as Int)
+
+        @Suppress("UNCHECKED_CAST")
+        val path = node.pathData as Array<PathNode>
+        assertEquals(3, path.size)
+        assertEquals(PathCommand.MoveTo, path.get(0).command)
+        assertEquals(20.0f, path.get(0).args[0])
+        assertEquals(10.0f, path.get(0).args[1])
+
+        assertEquals(PathCommand.RelativeLineTo, path.get(1).command)
+        assertEquals(6, path.get(1).args.size)
+        assertEquals(10.0f, path.get(1).args[0])
+        assertEquals(0.0f, path.get(1).args[1])
+        assertEquals(0.0f, path.get(1).args[2])
+        assertEquals(10.0f, path.get(1).args[3])
+        assertEquals(-10.0f, path.get(1).args[4])
+        assertEquals(0.0f, path.get(1).args[5])
+
+        assertEquals(PathCommand.RelativeClose, path.get(2).command)
+        assertEquals(0, path.get(2).args.size)
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-framework/src/androidTest/res/drawable/test_compose_vector.xml b/ui/ui-framework/src/androidTest/res/drawable/test_compose_vector.xml
new file mode 100644
index 0000000..05beeb3
--- /dev/null
+++ b/ui/ui-framework/src/androidTest/res/drawable/test_compose_vector.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFF0000"
+        android:pathData="M20,10, l10,0 0,10 -10, 0z"/>
+</vector>
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Clip.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Clip.kt
index be6e0ff..294325e 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Clip.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Clip.kt
@@ -27,7 +27,7 @@
  * @param shape the [Shape] used for clipping.
  */
 @Composable
-fun Clip(shape: Shape, @Children children: @Composable() () -> Unit) {
+fun Clip(shape: Shape, children: @Composable() () -> Unit) {
     <RepaintBoundaryNode name=null shape=shape clipToShape=true>
         children()
     </RepaintBoundaryNode>
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt
index 4686cc3..168452a 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt
@@ -516,7 +516,7 @@
  * Please note that using this widget might be a performance hit, so please use with care.
  */
 @Composable
-fun WithConstraints(@Children children: @Composable() (Constraints) -> Unit) {
+fun WithConstraints(children: @Composable() (Constraints) -> Unit) {
     val ref = +compositionReference()
     val context = +ambient(ContextAmbient)
 
@@ -592,7 +592,7 @@
 @Composable
 fun OnChildPositioned(
     onPositioned: (coordinates: LayoutCoordinates) -> Unit,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     <DataNode key=OnChildPositionedKey value=onPositioned>
         <children/>
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Opacity.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Opacity.kt
index 7399e1a..bc1881a 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Opacity.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Opacity.kt
@@ -33,7 +33,7 @@
 @Composable
 fun Opacity(
     @FloatRange(from = 0.0, to = 1.0) opacity: Float,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     <RepaintBoundaryNode name=null opacity=opacity>
         children()
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/ParentData.kt b/ui/ui-framework/src/main/java/androidx/ui/core/ParentData.kt
index 8ae7c49..081b3daf 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/ParentData.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/ParentData.kt
@@ -53,7 +53,7 @@
  * expanded, which are inflexible and which are flexible.
  */
 @Composable
-fun ParentData(data: Any, @Children children: @Composable() () -> Unit) {
+fun ParentData(data: Any, children: @Composable() () -> Unit) {
     <DataNode key=ParentDataKey value=data>
         <children/>
     </DataNode>
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/PointerInputWrapper.kt b/ui/ui-framework/src/main/java/androidx/ui/core/PointerInputWrapper.kt
index ec66af4..d6e0e77 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/PointerInputWrapper.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/PointerInputWrapper.kt
@@ -24,7 +24,7 @@
 @Composable
 fun PointerInputWrapper(
     pointerInputHandler: PointerInputHandler = { event, _ -> event },
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     // Hide the internals of PointerInputNode
     <PointerInputNode pointerInputHandler>
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/RepaintBoundary.kt b/ui/ui-framework/src/main/java/androidx/ui/core/RepaintBoundary.kt
index 757e740..cbf1760 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/RepaintBoundary.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/RepaintBoundary.kt
@@ -31,7 +31,7 @@
  * @param children The contained children.
  */
 @Composable
-fun RepaintBoundary(name: String? = null, @Children children: @Composable() () -> Unit) {
+fun RepaintBoundary(name: String? = null, children: @Composable() () -> Unit) {
     <RepaintBoundaryNode name=name>
         <children/>
     </RepaintBoundaryNode>
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Semantics.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Semantics.kt
index baa4ff3..dad6816 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Semantics.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Semantics.kt
@@ -124,7 +124,7 @@
     textDirection: TextDirection? = null,
     testTag: String? = null,
     actions: List<SemanticsAction<*>> = emptyList(),
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val providedTestTag = +ambient(TestTagAmbient)
     <SemanticsComponentNode
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/TestTagProvider.kt b/ui/ui-framework/src/main/java/androidx/ui/core/TestTagProvider.kt
index e69bf03..a75a022 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/TestTagProvider.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/TestTagProvider.kt
@@ -27,6 +27,6 @@
 // Implementation with ambients now for only one semantics inside.
 // replace with mergeable semantics later
 @Composable
-fun TestTag(tag: String, @Children children: @Composable() () -> Unit) {
+fun TestTag(tag: String, children: @Composable() () -> Unit) {
     TestTagAmbient.Provider(value = tag, children = children)
 }
\ No newline at end of file
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt
index 7e1680cb..4d0fd2a 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Text.kt
@@ -97,7 +97,7 @@
     /**
      * Composable TextSpan attached after [text].
      */
-    @Children child: @Composable TextSpanScope.() -> Unit
+    child: @Composable TextSpanScope.() -> Unit
 ) {
     val rootTextSpan = +memo(text) { TextSpan(text = text) }
     val ref = +compositionReference()
@@ -321,7 +321,7 @@
  * styled explicitly.
  */
 @Composable
-fun CurrentTextStyleProvider(value: TextStyle, @Children children: @Composable() () -> Unit) {
+fun CurrentTextStyleProvider(value: TextStyle, children: @Composable() () -> Unit) {
     val style = +ambient(CurrentTextStyleAmbient)
     val mergedStyle = style.merge(value)
     CurrentTextStyleAmbient.Provider(value = mergedStyle) {
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/InputField.kt b/ui/ui-framework/src/main/java/androidx/ui/core/TextField.kt
similarity index 89%
rename from ui/ui-framework/src/main/java/androidx/ui/core/InputField.kt
rename to ui/ui-framework/src/main/java/androidx/ui/core/TextField.kt
index 5da2696..1af15fc50 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/InputField.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/TextField.kt
@@ -28,7 +28,7 @@
 import androidx.ui.core.input.FocusManager
 import androidx.ui.graphics.Color
 import androidx.ui.input.EditProcessor
-import androidx.ui.input.EditorState
+import androidx.ui.input.EditorModel
 import androidx.ui.input.ImeAction
 import androidx.ui.input.KeyboardType
 import androidx.ui.text.TextPainter
@@ -44,35 +44,35 @@
     /**
      * The composition background color
      *
-     * @see EditorState.composition
+     * @see EditorModel.composition
      */
     val compositionColor: Color = Color(alpha = 0xFF, red = 0xB0, green = 0xE0, blue = 0xE6),
 
     /**
      *  The selection background color
      *
-     *  @see EditorState.selection
+     *  @see EditorModel.selection
      */
     // TODO(nona): share with Text.DEFAULT_SELECTION_COLOR
     val selectionColor: Color = Color(alpha = 0x66, red = 0x33, green = 0xB5, blue = 0xE5)
 )
 
 /**
- * A default implementation of InputField
+ * A default implementation of TextField
  *
- * To make InputField work with platoform input service, you must keep the editor state and update
+ * To make TextField work with platoform input service, you must keep the editor state and update
  * in [onValueChagne] callback.
  *
  * Example:
- *     var state = +state { EditorState() }
- *     InputField(
+ *     var state = +state { EditorModel() }
+ *     TextField(
  *         value = state.value,
  *         onValueChange = { state.value = it })
  */
 @Composable
-fun InputField(
+fun TextField(
     /** Initial editor state value */
-    value: EditorState,
+    value: EditorModel,
 
     /** The editor style */
     editorStyle: EditorStyle,
@@ -96,7 +96,7 @@
     imeAction: ImeAction = ImeAction.Unspecified,
 
     /** Called when the InputMethodService update the editor state */
-    onValueChange: (EditorState) -> Unit = {},
+    onValueChange: (EditorModel) -> Unit = {},
 
     /** Called when the InputMethod requested an IME action */
     onImeActionPerformed: (ImeAction) -> Unit = {},
@@ -116,7 +116,7 @@
     val processor = +memo { EditProcessor() }
     val mergedStyle = style.merge(editorStyle.textStyle)
     val (visualText, offsetMap) = +memo(value, visualTransformation) {
-        InputFieldDelegate.applyVisualFilter(value, visualTransformation)
+        TextFieldDelegate.applyVisualFilter(value, visualTransformation)
     }
     val textPainter = +memo(visualText, mergedStyle, density, resourceLoader) {
         // TODO(nona): Add parameter for text direction, softwrap, etc.
@@ -137,7 +137,7 @@
         onPress = { },
         onFocus = {
             hasFocus.value = true
-            InputFieldDelegate.onFocus(
+            TextFieldDelegate.onFocus(
                 textInputService,
                 value,
                 processor,
@@ -147,7 +147,7 @@
                 onImeActionPerformed)
             coords.value?.let { coords ->
                 textInputService?.let { textInputService ->
-                    InputFieldDelegate.notifyFocusedRect(
+                    TextFieldDelegate.notifyFocusedRect(
                         value,
                         textPainter,
                         coords,
@@ -160,14 +160,14 @@
         },
         onBlur = {
             hasFocus.value = false
-            InputFieldDelegate.onBlur(
+            TextFieldDelegate.onBlur(
                 textInputService,
                 processor,
                 onValueChange)
         },
-        onDragAt = { InputFieldDelegate.onDragAt(it) },
+        onDragAt = { TextFieldDelegate.onDragAt(it) },
         onRelease = {
-            InputFieldDelegate.onRelease(
+            TextFieldDelegate.onRelease(
                 it,
                 textPainter,
                 processor,
@@ -184,7 +184,7 @@
                         // TODO(nona): notify focused rect in onPreDraw equivalent callback for
                         //             supporting multiline text.
                         coords.value = it
-                        InputFieldDelegate.notifyFocusedRect(
+                        TextFieldDelegate.notifyFocusedRect(
                             value,
                             textPainter,
                             it,
@@ -194,7 +194,7 @@
                         )
                     }
                 }
-                Draw { canvas, _ -> InputFieldDelegate.draw(
+                Draw { canvas, _ -> TextFieldDelegate.draw(
                     canvas,
                     value,
                     offsetMap,
@@ -203,7 +203,7 @@
                     editorStyle) }
             },
             layoutBlock = { _, constraints ->
-                InputFieldDelegate.layout(textPainter, constraints).let {
+                TextFieldDelegate.layout(textPainter, constraints).let {
                     layout(it.first, it.second) {}
                 }
             }
@@ -221,7 +221,7 @@
     onRelease: (PxPosition) -> Unit,
     onFocus: () -> Unit,
     onBlur: () -> Unit,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val focused = +state { false }
     val focusManager = +ambient(FocusManagerAmbient)
@@ -294,7 +294,7 @@
     onPress: (PxPosition) -> Unit,
     onDragAt: (PxPosition) -> Unit,
     onRelease: (PxPosition) -> Unit,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val tracker = +state { DragEventTracker() }
     PressGestureDetector(
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/InputFieldDelegate.kt b/ui/ui-framework/src/main/java/androidx/ui/core/TextFieldDelegate.kt
similarity index 94%
rename from ui/ui-framework/src/main/java/androidx/ui/core/InputFieldDelegate.kt
rename to ui/ui-framework/src/main/java/androidx/ui/core/TextFieldDelegate.kt
index 1c70f23..9d66efa 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/InputFieldDelegate.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/TextFieldDelegate.kt
@@ -20,7 +20,7 @@
 import androidx.ui.engine.geometry.Rect
 import androidx.ui.input.EditOperation
 import androidx.ui.input.EditProcessor
-import androidx.ui.input.EditorState
+import androidx.ui.input.EditorModel
 import androidx.ui.input.FinishComposingTextEditOp
 import androidx.ui.input.ImeAction
 import androidx.ui.input.KeyboardType
@@ -30,7 +30,7 @@
 import androidx.ui.text.AnnotatedString
 import androidx.ui.text.TextPainter
 
-internal class InputFieldDelegate {
+internal class TextFieldDelegate {
     companion object {
         /**
          * Process text layout with given constraint.
@@ -80,7 +80,7 @@
         @JvmStatic
         fun draw(
             canvas: Canvas,
-            value: EditorState,
+            value: EditorModel,
             offsetMap: OffsetMap,
             textPainter: TextPainter,
             hasFocus: Boolean,
@@ -117,7 +117,7 @@
          */
         @JvmStatic
         fun notifyFocusedRect(
-            value: EditorState,
+            value: EditorModel,
             textPainter: TextPainter,
             layoutCoordinates: LayoutCoordinates,
             textInputService: TextInputService,
@@ -158,7 +158,7 @@
         internal fun onEditCommand(
             ops: List<EditOperation>,
             editProcessor: EditProcessor,
-            onValueChange: (EditorState) -> Unit
+            onValueChange: (EditorModel) -> Unit
         ) {
             onValueChange(editProcessor.onEditCommands(ops))
         }
@@ -171,7 +171,7 @@
         @JvmStatic
         fun onDragAt(position: PxPosition) {
             // TODO(nona): Implement this function
-            Log.d("InputFieldDelegate", "onDrag: $position")
+            Log.d("TextFieldDelegate", "onDrag: $position")
         }
 
         /**
@@ -191,7 +191,7 @@
             textPainter: TextPainter,
             editProcessor: EditProcessor,
             offsetMap: OffsetMap,
-            onValueChange: (EditorState) -> Unit,
+            onValueChange: (EditorModel) -> Unit,
             textInputService: TextInputService?,
             hasFocus: Boolean
         ) {
@@ -219,15 +219,15 @@
         @JvmStatic
         fun onFocus(
             textInputService: TextInputService?,
-            value: EditorState,
+            value: EditorModel,
             editProcessor: EditProcessor,
             keyboardType: KeyboardType,
             imeAction: ImeAction,
-            onValueChange: (EditorState) -> Unit,
+            onValueChange: (EditorModel) -> Unit,
             onImeActionPerformed: (ImeAction) -> Unit
         ) {
             textInputService?.startInput(
-                initState = value,
+                initModel = value,
                 keyboardType = keyboardType,
                 imeAction = imeAction,
                 onEditCommand = { onEditCommand(it, editProcessor, onValueChange) },
@@ -245,21 +245,21 @@
         fun onBlur(
             textInputService: TextInputService?,
             editProcessor: EditProcessor,
-            onValueChange: (EditorState) -> Unit
+            onValueChange: (EditorModel) -> Unit
         ) {
             onEditCommand(listOf(FinishComposingTextEditOp()), editProcessor, onValueChange)
             textInputService?.stopInput()
         }
 
         /**
-         * Helper function of applying visual transformation method to the EditorState.
+         * Helper function of applying visual transformation method to the EditorModel.
          *
          * @param value An editor state
          * @param visualTransformation A visual transformation
          */
         @JvmStatic
         fun applyVisualFilter(
-            value: EditorState,
+            value: EditorModel,
             visualTransformation: VisualTransformation?
         ): TransformedText {
             val annotatedString = AnnotatedString(value.text)
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/TextSpanCompose.kt b/ui/ui-framework/src/main/java/androidx/ui/core/TextSpanCompose.kt
index 1af1687..8d8a56f 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/TextSpanCompose.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/TextSpanCompose.kt
@@ -256,7 +256,7 @@
 fun TextSpanScope.Span(
     text: String? = null,
     style: TextStyle? = null,
-    @Children child: @Composable TextSpanScope.() -> Unit
+    child: @Composable TextSpanScope.() -> Unit
 ) {
     TextSpan(text = text, style = style) {
         child()
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Wrapper.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Wrapper.kt
index 45f178a..ed51529 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Wrapper.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Wrapper.kt
@@ -47,7 +47,7 @@
  * [Activity.setContent] or [ViewGroup.setContent] extensions.
  */
 @Composable
-fun ComposeView(@Children children: @Composable() () -> Unit) {
+fun ComposeView(children: @Composable() () -> Unit) {
     val rootRef = +memo { Ref<AndroidCraneView>() }
 
     <AndroidCraneView ref=rootRef>
@@ -100,7 +100,7 @@
  * @param content Composable that will be the content of the activity.
  */
 fun Activity.setContent(
-    @Children content: @Composable() () -> Unit
+    content: @Composable() () -> Unit
 ): CompositionContext? {
     val craneView = window.decorView
         .findViewById<ViewGroup>(android.R.id.content)
@@ -123,7 +123,7 @@
  * @param content Composable that will be the content of the view.
  */
 fun ViewGroup.setContent(
-    @Children content: @Composable() () -> Unit
+    content: @Composable() () -> Unit
 ): CompositionContext? {
     val craneView =
         if (childCount > 0) { getChildAt(0) as? AndroidCraneView } else { removeAllViews(); null }
@@ -144,7 +144,7 @@
     craneView: AndroidCraneView,
     context: Context,
     coroutineContext: CoroutineContext,
-    @Children content: @Composable() () -> Unit
+    content: @Composable() () -> Unit
 ) {
     // TODO(nona): Tie the focus manger lifecycle to Window, otherwise FocusManager won't work
     //             with nested AndroidCraneView case
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/DragGestureDetector.kt b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/DragGestureDetector.kt
index 95cc8eb..0692d57 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/DragGestureDetector.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/DragGestureDetector.kt
@@ -100,7 +100,7 @@
 fun DragGestureDetector(
     canDrag: ((Direction) -> Boolean)? = null,
     dragObserver: DragObserver? = null,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val recognizer = +memo { DragGestureRecognizer() }
     recognizer.canDrag = canDrag
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/LongPressGestureDetector.kt b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/LongPressGestureDetector.kt
index a3f894c..d269572 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/LongPressGestureDetector.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/LongPressGestureDetector.kt
@@ -46,7 +46,7 @@
 @Composable
 fun LongPressGestureDetector(
     onLongPress: (PxPosition) -> Unit,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val recognizer =
         +memo { LongPressGestureRecognizer(onLongPress, +ambient(CoroutineContextAmbient)) }
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressGestureDetector.kt b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressGestureDetector.kt
index 6d8beb2..e91a1e34 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressGestureDetector.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressGestureDetector.kt
@@ -26,7 +26,7 @@
     onPress: ((PxPosition) -> Unit)? = null,
     onRelease: (() -> Unit)? = null,
     onCancel: (() -> Unit)? = null,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     PressIndicatorGestureDetector(onStart = onPress, onCancel = onCancel) {
         PressReleasedGestureDetector(onRelease = onRelease, consumeDownOnStart = false) {
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressIndicatorGestureDetector.kt b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressIndicatorGestureDetector.kt
index 7705035..423b917 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressIndicatorGestureDetector.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressIndicatorGestureDetector.kt
@@ -53,7 +53,7 @@
     onStart: ((PxPosition) -> Unit)? = null,
     onStop: (() -> Unit)? = null,
     onCancel: (() -> Unit)? = null,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val recognizer = +memo { PressIndicatorGestureRecognizer() }
     recognizer.onStart = onStart
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressReleasedGestureDetector.kt b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressReleasedGestureDetector.kt
index a524b3f..99a0daa 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressReleasedGestureDetector.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/PressReleasedGestureDetector.kt
@@ -52,7 +52,7 @@
 fun PressReleasedGestureDetector(
     onRelease: (() -> Unit)? = null,
     consumeDownOnStart: Boolean = true,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val recognizer = +memo { PressReleaseGestureRecognizer() }
     recognizer.onRelease = onRelease
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
index a3bd5e5..6dfb42c 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
@@ -44,7 +44,7 @@
     onSelectionChange: (Selection?) -> Unit,
     /** Selection mode. The default mode is Vertical. */
     mode: SelectionMode = SelectionMode.Vertical,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val manager = +memo { SelectionManager() }
     // TODO (qqd): After selection widget is fully implemented, evaluate if the following 2 items
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/Vector.kt b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/Vector.kt
index 9fd6bb1..fcf26ac 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/Vector.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/Vector.kt
@@ -34,17 +34,18 @@
 import androidx.compose.Composable
 import androidx.compose.Emittable
 import androidx.compose.composer
+import androidx.ui.core.Px
 import androidx.ui.painting.withSave
 import androidx.ui.painting.Path as PaintingPath
 
 const val DefaultGroupName = ""
-const val DefaultRotate = 0.0f
+const val DefaultRotation = 0.0f
 const val DefaultPivotX = 0.0f
 const val DefaultPivotY = 0.0f
 const val DefaultScaleX = 1.0f
 const val DefaultScaleY = 1.0f
-const val DefaultTranslateX = 0.0f
-const val DefaultTranslateY = 0.0f
+const val DefaultTranslationX = 0.0f
+const val DefaultTranslationY = 0.0f
 
 val EmptyPath = emptyArray<PathNode>()
 
@@ -80,50 +81,55 @@
     }
 
 @Composable
-fun vector(
+fun Vector(
     name: String = "",
     viewportWidth: Float,
     viewportHeight: Float,
-    defaultWidth: Float = viewportWidth,
-    defaultHeight: Float = viewportHeight,
-    @Children children: @Composable() () -> Unit
+    defaultWidth: Px = Px(viewportWidth),
+    defaultHeight: Px = Px(viewportHeight),
+    children: @Composable() () -> Unit
 ) {
-    <Vector name defaultWidth defaultHeight viewportWidth viewportHeight>
+    <VectorComponent
+        name
+        defaultWidth = defaultWidth.value
+        defaultHeight = defaultHeight.value
+        viewportWidth
+        viewportHeight>
         children()
-    </Vector>
+    </VectorComponent>
 }
 
 @Composable
-fun group(
+fun Group(
     name: String = DefaultGroupName,
-    rotate: Float = DefaultRotate,
+    rotation: Float = DefaultRotation,
     pivotX: Float = DefaultPivotX,
     pivotY: Float = DefaultPivotY,
     scaleX: Float = DefaultScaleX,
     scaleY: Float = DefaultScaleY,
-    translateX: Float = DefaultTranslateX,
-    translateY: Float = DefaultTranslateY,
+    translationX: Float = DefaultTranslationX,
+    translationY: Float = DefaultTranslationY,
     clipPathData: PathData = EmptyPath,
-    @Children childNodes: @Composable() () -> Unit
+    childNodes: @Composable() () -> Unit
 ) {
 
     val clipPathNodes = createPath(clipPathData)
-    <Group
+    <GroupComponent
         name
-        rotate
+        rotation
         pivotX
         pivotY
         scaleX
         scaleY
-        translateX
-        translateY
+        translationX
+        translationY
         clipPathNodes>
         childNodes()
-    </Group>
+    </GroupComponent>
 }
 
 @Composable
-fun path(
+fun Path(
     pathData: PathData,
     name: String = DefaultPathName,
     fill: BrushType = EmptyBrush,
@@ -139,7 +145,7 @@
     val fillBrush: Brush = obtainBrush(fill)
     val strokeBrush: Brush = obtainBrush(stroke)
 
-    <Path
+    <PathComponent
         name
         pathNodes
         fill=fillBrush
@@ -156,7 +162,7 @@
     abstract fun draw(canvas: Canvas)
 }
 
-private class Vector(
+private class VectorComponent(
     val name: String = "",
     val viewportWidth: Float,
     val viewportHeight: Float,
@@ -164,7 +170,7 @@
     val defaultHeight: Float = viewportHeight
 ) : VNode(), Emittable {
 
-    private val root = Group(this@Vector.name).apply {
+    private val root = GroupComponent(this@VectorComponent.name).apply {
         pivotX = 0.0f
         pivotY = 0.0f
         scaleX = defaultWidth / viewportWidth
@@ -218,7 +224,7 @@
     }
 }
 
-private class Path(val name: String) : VNode(), Emittable {
+private class PathComponent(val name: String) : VNode(), Emittable {
 
     var fill: Brush = EmptyBrush
         set(value) {
@@ -383,7 +389,7 @@
     }
 }
 
-private class Group(val name: String = DefaultGroupName) : VNode(), Emittable {
+private class GroupComponent(val name: String = DefaultGroupName) : VNode(), Emittable {
 
     private var groupMatrix: Matrix? = null
 
@@ -425,7 +431,7 @@
         }
     }
 
-    var rotate: Float = DefaultRotate
+    var rotation: Float = DefaultRotation
         set(value) {
             field = value
             isMatrixDirty = true
@@ -455,13 +461,13 @@
             isMatrixDirty = true
         }
 
-    var translateX: Float = DefaultTranslateX
+    var translationX: Float = DefaultTranslationX
         set(value) {
             field = value
             isMatrixDirty = true
         }
 
-    var translateY: Float = DefaultTranslateY
+    var translationY: Float = DefaultTranslationY
         set(value) {
             field = value
             isMatrixDirty = true
@@ -482,9 +488,9 @@
             reset()
             postTranslate(-pivotX, -pivotY)
             postScale(scaleX, scaleY)
-            postRotate(rotate, 0f, 0f)
-            postTranslate(translateX + pivotX,
-                translateY + pivotY)
+            postRotate(rotation, 0f, 0f)
+            postTranslate(translationX + pivotX,
+                translationY + pivotY)
         }
     }
 
@@ -579,7 +585,7 @@
 
 // Temporary glue logic to wrap a Vector asset into an ImageView
 fun adoptVectorGraphic(parent: Any?, child: Any?): View? {
-    return if (parent is ViewGroup && child is Vector) {
+    return if (parent is ViewGroup && child is VectorComponent) {
         val imageView = ImageView(parent.context)
         imageView.scaleType = ImageView.ScaleType.FIT_CENTER
         imageView.setImageDrawable(VectorGraphicDrawable(child))
@@ -589,7 +595,7 @@
     }
 }
 
-private class VectorGraphicDrawable(private val vector: Vector) : Drawable() {
+private class VectorGraphicDrawable(private val vector: VectorComponent) : Drawable() {
 
     override fun getIntrinsicWidth(): Int = Math.round(vector.defaultWidth)
 
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/VectorAsset.kt b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/VectorAsset.kt
new file mode 100644
index 0000000..95f732b
--- /dev/null
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/VectorAsset.kt
@@ -0,0 +1,420 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.core.vectorgraphics
+
+import androidx.compose.Composable
+import androidx.compose.composer
+import androidx.ui.core.Px
+import androidx.ui.painting.StrokeCap
+import androidx.ui.painting.StrokeJoin
+import java.util.Stack
+import java.util.function.Consumer
+
+/**
+ * Builder used to construct a Vector graphic tree.
+ * This is useful for caching the result of expensive operations used to construct
+ * a vector graphic for compose.
+ * For example, the vector graphic could be serialized and downloaded from a server and represented
+ * internally in a VectorAsset before it is composed through [DrawVector]
+ * The generated VectorAsset is recommended to be memoized across composition calls to avoid
+ * doing redundant work
+ **/
+class VectorAssetBuilder(
+
+    /**
+     * Name of the vector asset
+     */
+    val name: String = DefaultGroupName,
+
+    /**
+     * Intrinsic width of the Vector in pixels
+     */
+    val defaultWidth: Px,
+
+    /**
+     * Intrinsic height of the Vector in pixels
+     */
+    val defaultHeight: Px,
+
+    /**
+     *  Used to define the width of the viewport space. Viewport is basically the virtual canvas
+     *  where the paths are drawn on.
+     */
+    val viewportWidth: Float,
+
+    /**
+     * Used to define the height of the viewport space. Viewport is basically the virtual canvas
+     * where the paths are drawn on.
+     */
+    val viewportHeight: Float
+) {
+
+    private val nodes = Stack<VectorGroup>()
+
+    private var root = VectorGroup()
+    private var isConsumed = false
+
+    private var currentGroup: VectorGroup = root
+        private set
+        get() = nodes.peek()
+
+    init {
+        nodes.add(root)
+    }
+
+    /**
+     * Create a new group and push it to the front of the stack of VectorAsset nodes
+     * @return This VectorAssetBuilder instance as a convenience for chaining calls
+     */
+    fun pushGroup(
+        name: String = DefaultGroupName,
+        rotate: Float = DefaultRotation,
+        pivotX: Float = DefaultPivotX,
+        pivotY: Float = DefaultPivotY,
+        scaleX: Float = DefaultScaleX,
+        scaleY: Float = DefaultScaleY,
+        translationX: Float = DefaultTranslationX,
+        translationY: Float = DefaultTranslationY,
+        clipPathData: PathData = EmptyPath
+    ): VectorAssetBuilder {
+        ensureNotConsumed()
+        val group = VectorGroup(
+                name,
+                rotate,
+                pivotX,
+                pivotY,
+                scaleX,
+                scaleY,
+                translationX,
+                translationY,
+                clipPathData
+        )
+        nodes.add(group)
+        currentGroup.addNode(group)
+        return this
+    }
+
+    /**
+     * Pops the topmost VectorGroup from this VectorAssetBuilder. This is used to indicate
+     * that no additional VectorAsset nodes will be added to the current VectorGroup
+     * @return This VectorAssetBuilder instance as a convenience for chaining calls
+     */
+    fun popGroup(): VectorAssetBuilder {
+        ensureNotConsumed()
+        nodes.pop()
+        return this
+    }
+
+    /**
+     * Add a path to the VectorAsset graphic. This represents a leaf node in the VectorAsset graphics
+     * tree structure
+     * @return This VectorAssetBuilder instance as a convenience for chaining calls
+     */
+    fun addPath(
+        pathData: PathData,
+        name: String = DefaultPathName,
+        fill: BrushType = EmptyBrush,
+        fillAlpha: Float = DefaultAlpha,
+        stroke: BrushType = EmptyBrush,
+        strokeAlpha: Float = DefaultAlpha,
+        strokeLineWidth: Float = DefaultStrokeLineWidth,
+        strokeLineCap: StrokeCap = DefaultStrokeLineCap,
+        strokeLineJoin: StrokeJoin = DefaultStrokeLineJoin,
+        strokeLineMiter: Float = DefaultStrokeLineMiter
+    ): VectorAssetBuilder {
+        ensureNotConsumed()
+        currentGroup.addNode(
+                VectorPath(
+                        name,
+                        pathData,
+                        fill,
+                        fillAlpha,
+                        stroke,
+                        strokeAlpha,
+                        strokeLineWidth,
+                        strokeLineCap,
+                        strokeLineJoin,
+                        strokeLineMiter
+                )
+        )
+        return this
+    }
+
+    /**
+     * Construct a VectorAsset. This concludes the creation process of a VectorAsset graphic
+     * This builder cannot be re-used to create additional VectorAsset instances
+     * @return Thew newly created VectorAsset instance
+     */
+    fun build(): VectorAsset {
+        ensureNotConsumed()
+        val vectorImage = VectorAsset(
+            name,
+            defaultWidth,
+            defaultHeight,
+            viewportWidth,
+            viewportHeight,
+            root
+        )
+
+        // reset state in case this builder is used again
+        nodes.clear()
+        root = VectorGroup()
+        nodes.add(root)
+
+        isConsumed = true
+
+        return vectorImage
+    }
+
+    /**
+     * Throws IllegalStateException if the VectorAssetBuilder is already been consumed
+     */
+    fun ensureNotConsumed() {
+        if (isConsumed) {
+            throw IllegalStateException("VectorAssetBuilder is single use, create " +
+                    "a new instance to create a new VectorAsset")
+        }
+    }
+}
+
+sealed class VectorNode
+
+/**
+ * Vector graphics object that is generated as a result of [VectorAssetBuilder]]
+ * It can be composed and rendered by passing it as an argument to [DrawVector]
+ */
+class VectorAsset internal constructor(
+
+    /**
+     * Name of the Vector asset
+     */
+    val name: String,
+
+    /**
+     * Intrinsic width of the vector asset in pixels
+     */
+    val defaultWidth: Px,
+
+    /**
+     * Intrinsic height of the vector asset in pixels
+     */
+    val defaultHeight: Px,
+
+    /**
+     *  Used to define the width of the viewport space. Viewport is basically the virtual canvas
+     *  where the paths are drawn on.
+     */
+    val viewportWidth: Float,
+
+    /**
+     * Used to define the height of the viewport space. Viewport is basically the virtual canvas
+     * where the paths are drawn on.
+     */
+    val viewportHeight: Float,
+
+    /**
+     * Root group of the vector asset that contains all the child groups and paths
+     */
+    val root: VectorGroup
+)
+
+/**
+ * Defines a group of paths or subgroups, plus transformation information.
+ * The transformations are defined in the same coordinates as the viewport.
+ * The transformations are applied in the order of scale, rotate then translate.
+ */
+class VectorGroup(
+    /**
+     * Name of the corresponding group
+     */
+    val name: String = DefaultGroupName,
+
+    /**
+     * Rotation of the group in degrees
+     */
+    val rotation: Float = DefaultRotation,
+
+    /**
+     * X coordinate of the pivot point to rotate or scale the group
+     */
+    val pivotX: Float = DefaultPivotX,
+
+    /**
+     * Y coordinate of the pivot point to rotate or scale the group
+     */
+    val pivotY: Float = DefaultPivotY,
+
+    /**
+     * Scale factor in the X-axis to apply to the group
+     */
+    val scaleX: Float = DefaultScaleX,
+
+    /**
+     * Scale factor in the Y-axis to apply to the group
+     */
+    val scaleY: Float = DefaultScaleY,
+
+    /**
+     * Translation in virtual pixels to apply along the x-axis
+     */
+    val translationX: Float = DefaultTranslationX,
+
+    /**
+     * Translation in virtual pixels to apply along the y-axis
+     */
+    val translationY: Float = DefaultTranslationY,
+
+    /**
+     * Path information used to clip the content within the group
+     */
+    val clipPathData: PathData = EmptyPath
+
+) : VectorNode(), Iterable<VectorNode> {
+
+    private val children = ArrayList<VectorNode>()
+
+    internal fun addNode(node: VectorNode) {
+        children.add(node)
+    }
+
+    val size: Int
+        get() = children.size
+
+    operator fun get(index: Int): VectorNode {
+        return children[index]
+    }
+
+    override fun iterator(): Iterator<VectorNode> {
+        return object: Iterator<VectorNode> {
+
+            val it = children.iterator()
+
+            override fun hasNext(): Boolean = it.hasNext()
+
+            override fun next(): VectorNode = it.next()
+        }
+    }
+}
+
+/**
+ * Leaf node of a Vector graphics tree. This specifies a path shape and parameters
+ * to color and style the the shape itself
+ */
+class VectorPath(
+    /**
+     * Name of the corresponding path
+     */
+    val name: String = DefaultPathName,
+
+    /**
+     * Path information to render the shape of the path
+     */
+    val pathData: PathData,
+
+    /**
+     *  Specifies the color or gradient used to fill the path
+     */
+    val fill: BrushType = EmptyBrush,
+
+    /**
+     * Opacity to fill the path
+     */
+    val fillAlpha: Float = DefaultAlpha,
+
+    /**
+     * Specifies the color or gradient used to fill the stroke
+     */
+    val stroke: BrushType = EmptyBrush,
+
+    /**
+     * Opacity to stroke the path
+     */
+    val strokeAlpha: Float = DefaultAlpha,
+
+    /**
+     * Width of the line to stroke the path
+     */
+    val strokeLineWidth: Float = DefaultStrokeLineWidth,
+
+    /**
+     * Specifies the linecap for a stroked path, either butt, round, or square. The default is butt.
+     */
+    val strokeLineCap: StrokeCap = DefaultStrokeLineCap,
+
+    /**
+     * Specifies the linejoin for a stroked path, either miter, round or bevel. The default is miter
+     */
+    val strokeLineJoin: StrokeJoin = DefaultStrokeLineJoin,
+
+    /**
+     * Specifies the miter limit for a stroked path, the default is 4
+     */
+    val strokeLineMiter: Float = DefaultStrokeLineMiter
+) : VectorNode()
+
+/**
+ * Composes a vector graphic into the composition tree based on the specification
+ * provided by given [VectorAsset]
+ */
+@Composable
+fun DrawVector(vectorImage: VectorAsset) {
+    Vector(
+        name = vectorImage.name,
+        viewportWidth = vectorImage.viewportWidth,
+        viewportHeight = vectorImage.viewportHeight,
+        defaultWidth = vectorImage.defaultWidth,
+        defaultHeight = vectorImage.defaultHeight) {
+        RenderVectorGroup(group = vectorImage.root)
+    }
+}
+
+/**
+ * Recursive method for creating the vector graphic composition by traversing
+ * the tree structure
+ */
+@Composable
+private fun RenderVectorGroup(group: VectorGroup) {
+    for (vectorNode in group) {
+        if (vectorNode is VectorPath) {
+            Path(
+                pathData = vectorNode.pathData,
+                name = vectorNode.name,
+                fill = vectorNode.fill,
+                fillAlpha = vectorNode.fillAlpha,
+                stroke = vectorNode.stroke,
+                strokeAlpha = vectorNode.strokeAlpha,
+                strokeLineWidth = vectorNode.strokeLineWidth,
+                strokeLineCap = vectorNode.strokeLineCap,
+                strokeLineJoin = vectorNode.strokeLineJoin,
+                strokeLineMiter = vectorNode.strokeLineMiter
+            )
+        } else if (vectorNode is VectorGroup) {
+            Group(
+                name = vectorNode.name,
+                rotation = vectorNode.rotation,
+                scaleX = vectorNode.scaleX,
+                scaleY = vectorNode.scaleY,
+                translationX = vectorNode.translationX,
+                translationY = vectorNode.translationY,
+                pivotX = vectorNode.pivotX,
+                pivotY = vectorNode.pivotY,
+                clipPathData = vectorNode.clipPathData) {
+                RenderVectorGroup(group = vectorNode)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/VectorResource.kt b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/VectorResource.kt
index c129e97..a886ad0 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/VectorResource.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/VectorResource.kt
@@ -16,395 +16,41 @@
 
 package androidx.ui.core.vectorgraphics.compat
 
+import androidx.compose.composer
 import android.annotation.SuppressLint
 import android.content.res.Resources
-import android.content.res.TypedArray
-import android.util.AttributeSet
-import android.util.Log
 import android.util.Xml
-import androidx.core.content.res.TypedArrayUtils
-import androidx.ui.core.vectorgraphics.addPathNodes
-import androidx.ui.core.vectorgraphics.group
-import androidx.ui.core.vectorgraphics.path
-import androidx.ui.core.vectorgraphics.vector
-import androidx.ui.painting.StrokeCap
-import androidx.ui.painting.StrokeJoin
-import androidx.ui.core.vectorgraphics.DefaultPivotX
-import androidx.ui.core.vectorgraphics.DefaultPivotY
-import androidx.ui.core.vectorgraphics.DefaultRotate
-import androidx.ui.core.vectorgraphics.DefaultScaleX
-import androidx.ui.core.vectorgraphics.DefaultScaleY
-import androidx.ui.core.vectorgraphics.DefaultTranslateX
-import androidx.ui.core.vectorgraphics.DefaultTranslateY
-import androidx.ui.core.vectorgraphics.EmptyBrush
-import androidx.ui.core.vectorgraphics.EmptyPath
-import androidx.ui.core.vectorgraphics.PathNode
-import androidx.compose.Children
 import androidx.compose.Composable
-import androidx.compose.composer
-import org.xmlpull.v1.XmlPullParser
+import androidx.compose.ambient
+import androidx.compose.memo
+import androidx.compose.unaryPlus
+import androidx.ui.core.ContextAmbient
+import androidx.ui.core.vectorgraphics.DrawVector
+import androidx.ui.core.vectorgraphics.VectorAsset
 import org.xmlpull.v1.XmlPullParserException
-import java.io.IOException
-
-private val LINECAP_BUTT = 0
-private val LINECAP_ROUND = 1
-private val LINECAP_SQUARE = 2
-
-private val LINEJOIN_MITER = 0
-private val LINEJOIN_ROUND = 1
-private val LINEJOIN_BEVEL = 2
-
-private val FILL_TYPE_WINDING = 0
-
-private val SHAPE_CLIP_PATH = "clip-VPath"
-private val SHAPE_GROUP = "group"
-private val SHAPE_PATH = "path"
-
-private val LOGTAG = "VectorGraphicCreator"
-
-private fun getStrokeLineCap(id: Int, defValue: StrokeCap = StrokeCap.butt): StrokeCap =
-    when (id) {
-        LINECAP_BUTT -> StrokeCap.butt
-        LINECAP_ROUND -> StrokeCap.round
-        LINECAP_SQUARE -> StrokeCap.square
-        else -> defValue
-    }
-
-private fun getStrokeLineJoin(id: Int, defValue: StrokeJoin = StrokeJoin.miter): StrokeJoin =
-    when (id) {
-        LINEJOIN_MITER -> StrokeJoin.miter
-        LINEJOIN_ROUND -> StrokeJoin.round
-        LINEJOIN_BEVEL -> StrokeJoin.bevel
-        else -> defValue
-    }
 
 @Composable
+fun VectorResource(resId: Int) {
+    val context = +ambient(ContextAmbient)
+    val res = context.resources
+    val theme = context.theme
+    val vectorImage = +memo(resId) {
+        loadVectorResource(theme, res, resId)
+    }
+    DrawVector(vectorImage = vectorImage)
+}
+
+@Throws(XmlPullParserException::class)
 @SuppressWarnings("RestrictedApi")
-private fun inflateGroup(
-    a: TypedArray,
-    parser: XmlPullParser,
-    @Suppress("UNUSED_PARAMETER") theme: Resources.Theme?,
-    @Children childNodes: @Composable() () -> Unit
-) {
-    // Account for any configuration changes.
-    // mChangingConfigurations |= Utils.getChangingConfigurations(a);
+fun loadVectorResource(theme: Resources.Theme? = null, res: Resources, resId: Int): VectorAsset {
 
-    // Extract the theme attributes, if any.
-    // mThemeAttrs = null // TODO TINT THEME Not supported yet a.extractThemeAttrs();
-
-    // This is added in API 11
-    val rotate = TypedArrayUtils.getNamedFloat(
-        a, parser, "rotation",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_ROTATION,
-        DefaultRotate
-    )
-
-    val pivotX = a.getFloat(
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_X,
-        DefaultPivotX
-    )
-    val pivotY = a.getFloat(
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_Y,
-        DefaultPivotY
-    )
-
-    // This is added in API 11
-    val scaleX = TypedArrayUtils.getNamedFloat(
-        a, parser, "scaleX",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_X,
-        DefaultScaleX
-    )
-
-    // This is added in API 11
-    val scaleY = TypedArrayUtils.getNamedFloat(
-        a, parser, "scaleY",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_Y,
-        DefaultScaleY
-    )
-
-    val translateX = TypedArrayUtils.getNamedFloat(
-        a, parser, "translateX",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_X,
-        DefaultTranslateX
-    )
-    val translateY = TypedArrayUtils.getNamedFloat(
-        a, parser, "translateY",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_Y,
-        DefaultTranslateY
-    )
-
-    val name: String =
-        a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_NAME) ?: ""
-
-    parser.next()
-
-    // TODO parse clip path
-    val clipPathData = EmptyPath
-    group(
-        name = name,
-        rotate = rotate,
-        scaleX = scaleX,
-        scaleY = scaleY,
-        translateX = translateX,
-        translateY = translateY,
-        pivotX = pivotX,
-        pivotY = pivotY,
-        clipPathData = clipPathData
-    ) {
-        childNodes()
-    }
-}
-
-@Suppress("UNUSED_PARAMETER")
-@Composable
-private fun inflateClip(
-    a: TypedArray,
-    parser: XmlPullParser,
-    theme: Resources.Theme?,
-    @Children childNodes: @Composable() () -> Unit
-) {
-//    var pathName: String? =
-//        a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH_NAME)
-//    if (pathName == null) {
-//        pathName = ""
-//    }
-//    val pathData = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH_PATH_DATA)
-
-    // TODO (njawad) finish parsing clip paths from xml resources
-}
-
-@Composable
-@SuppressWarnings("RestrictedApi")
-private fun inflatePath(a: TypedArray, parser: XmlPullParser, theme: Resources.Theme?) {
-    val hasPathData = TypedArrayUtils.hasAttribute(parser, "pathData")
-    if (!hasPathData) {
-        // If there is no pathData in the VPath tag, then this is an empty VPath,
-        // nothing need to be drawn.
-        Log.v("VectorPath", "no path data available skipping path")
-        return
-    }
-
-    val name: String = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_NAME) ?: ""
-
-    val pathStr = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_PATH_DATA)
-
-    val pathData: Array<PathNode> = addPathNodes(pathStr)
-
-    val fillColor = TypedArrayUtils.getNamedComplexColor(
-        a, parser, theme, "fillColor",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_COLOR, 0
-    )
-    // TODO(njawad): restore these when they are used
-/*    val fillAlpha = TypedArrayUtils.getNamedFloat(
-        a, parser, "fillAlpha",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_ALPHA, 1.0f
-    )
-    val lineCap = TypedArrayUtils.getNamedInt(
-        a, parser, "strokeLineCap",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_CAP, -1
-    )
-    val strokeLineCap =
-        getStrokeLineCap(lineCap, StrokeCap.butt)
-    val lineJoin = TypedArrayUtils.getNamedInt(
-        a, parser, "strokeLineJoin",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_JOIN, -1
-    )
-    val strokeLineJoin =
-        getStrokeLineJoin(lineJoin, StrokeJoin.bevel)
-    val strokeMiterlimit = TypedArrayUtils.getNamedFloat(
-        a, parser, "strokeMiterLimit",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_MITER_LIMIT,
-        1.0f
-    )*/
-    val strokeColor = TypedArrayUtils.getNamedComplexColor(
-        a, parser, theme, "strokeColor",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_COLOR, 0
-    )
-    val strokeAlpha = TypedArrayUtils.getNamedFloat(
-        a, parser, "strokeAlpha",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_ALPHA, 1.0f
-    )
-    val strokeLineWidth = TypedArrayUtils.getNamedFloat(
-        a, parser, "strokeWidth",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_WIDTH, 1.0f
-    )
-    /*val trimPathEnd = TypedArrayUtils.getNamedFloat(
-        a, parser, "trimPathEnd",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_END, 1.0f
-    )
-    val trimPathOffset = TypedArrayUtils.getNamedFloat(
-        a, parser, "trimPathOffset",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_OFFSET,
-        0.0f
-    )
-    val trimPathStart = TypedArrayUtils.getNamedFloat(
-        a, parser, "trimPathStart",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_START,
-        0.0f
-    )
-    val fillRule = TypedArrayUtils.getNamedInt(
-        a, parser, "fillType",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_FILLTYPE,
-        FILL_TYPE_WINDING
-    )*/
-
-    // TODO update path with additional params
-    path(
-        name = name,
-        fill = if (fillColor.willDraw()) fillColor.color else EmptyBrush,
-        strokeAlpha = strokeAlpha,
-        strokeLineWidth = strokeLineWidth,
-        stroke = if (strokeColor.willDraw()) strokeColor.color else EmptyBrush,
-        pathData = pathData
-    )
-}
-
-@Composable
-@SuppressWarnings("RestrictedApi")
-private fun inflateInner(
-    res: Resources,
-    attrs: AttributeSet,
-    theme: Resources.Theme?,
-    innerDepth: Int,
-    parser: XmlPullParser
-) {
-    var eventType = parser.getEventType()
-
-    // Parse everything until the end of the VectorGraphic element.
-    while (eventType != XmlPullParser.END_DOCUMENT &&
-        (parser.getDepth() >= innerDepth || eventType != XmlPullParser.END_TAG)
-    ) {
-        if (eventType == XmlPullParser.START_TAG) {
-            val tagName = parser.getName()
-            if (SHAPE_PATH.equals(tagName)) {
-                Log.v("VectorPath", "parsing path...")
-                val a = TypedArrayUtils.obtainAttributes(
-                    res, theme, attrs,
-                    AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH
-                )
-                inflatePath(a = a, parser = parser, theme = theme)
-                a.recycle()
-            } else if (SHAPE_CLIP_PATH.equals(tagName)) {
-                val a = TypedArrayUtils.obtainAttributes(
-                    res, theme, attrs,
-                    AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH
-                )
-                inflateClip(a = a, parser = parser, theme = theme) {
-                    inflateInner(
-                        res = res,
-                        parser = parser,
-                        attrs = attrs,
-                        theme = theme,
-                        innerDepth = innerDepth
-                    )
-                }
-                a.recycle()
-            } else if (SHAPE_GROUP.equals(tagName)) {
-                val a = TypedArrayUtils.obtainAttributes(
-                    res, theme, attrs,
-                    AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP
-                )
-                inflateGroup(a = a, parser = parser, theme = theme) {
-                    inflateInner(
-                        res = res,
-                        parser = parser,
-                        attrs = attrs,
-                        theme = theme,
-                        innerDepth = innerDepth
-                    )
-                }
-                a.recycle()
-            }
-        }
-        eventType = parser.next()
-    }
-}
-
-@Composable
-@SuppressWarnings("RestrictedApi")
-private fun inflate(
-    res: Resources,
-    parser: XmlPullParser,
-    attrs: AttributeSet,
-    theme: Resources.Theme?
-) {
-    val innerDepth = parser.getDepth() + 1
-
-    val vectorAttrs = TypedArrayUtils.obtainAttributes(
-        res, theme, attrs,
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_TYPE_ARRAY
-    )
-
-    // TODO (njawad) handle mirroring here
-//        state.mAutoMirrored = TypedArrayUtils.getNamedBoolean(a, parser, "autoMirrored",
-//                AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_AUTO_MIRRORED, state.mAutoMirrored)
-
-    val viewportWidth = TypedArrayUtils.getNamedFloat(
-        vectorAttrs, parser, "viewportWidth",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_WIDTH,
-        0.0f
-    )
-
-    val viewportHeight = TypedArrayUtils.getNamedFloat(
-        vectorAttrs, parser, "viewportHeight",
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_HEIGHT,
-        0.0f
-    )
-
-    if (viewportWidth <= 0) {
-        throw XmlPullParserException(
-            vectorAttrs.getPositionDescription() +
-                    "VectorGraphic requires viewportWidth > 0"
-        )
-    } else if (viewportHeight <= 0) {
-        throw XmlPullParserException(
-            vectorAttrs.getPositionDescription() +
-                    "VectorGraphic requires viewportHeight > 0"
-        )
-    }
-
-    val defaultWidth = vectorAttrs.getDimension(
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_WIDTH, 0.0f
-    )
-    val defaultHeight = vectorAttrs.getDimension(
-        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_HEIGHT, 0.0f
-    )
-
-    vector(
-        name = "vector",
-        defaultWidth = defaultWidth,
-        defaultHeight = defaultHeight,
-        viewportWidth = viewportWidth,
-        viewportHeight = viewportHeight
-    ) {
-        inflateInner(
-            res = res,
-            attrs = attrs,
-            theme = theme,
-            innerDepth = innerDepth,
-            parser = parser
-        )
-    }
-}
-
-@Composable
-fun vectorResource(res: Resources, resId: Int) {
     @SuppressLint("ResourceType") val parser = res.getXml(resId)
     val attrs = Xml.asAttributeSet(parser)
-    var type: Int
-    try {
-        type = parser.next()
-        while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
-            // Empty loop
-            type = parser.next()
-        }
-        if (type != XmlPullParser.START_TAG) {
-            throw XmlPullParserException("No start tag found")
-        }
-        inflate(res = res, parser = parser, attrs = attrs, theme = null)
-    } catch (e: XmlPullParserException) {
-        Log.e(LOGTAG, "parser error", e)
-    } catch (e: IOException) {
-        Log.e(LOGTAG, "parser error", e)
+    val builder = parser.seekToStartTag().createVectorImageBuilder(res, theme, attrs)
+
+    while (!parser.isAtEnd()) {
+        parser.parseCurrentVectorNode(res, attrs, theme, builder)
+        parser.next()
     }
-}
+    return builder.build()
+}
\ No newline at end of file
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/XmlVectorParser.kt b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/XmlVectorParser.kt
new file mode 100644
index 0000000..121a723
--- /dev/null
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/vectorgraphics/compat/XmlVectorParser.kt
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.core.vectorgraphics.compat
+
+import android.content.res.Resources
+import android.util.AttributeSet
+import androidx.core.content.res.TypedArrayUtils
+import androidx.ui.core.Px
+import androidx.ui.core.vectorgraphics.DefaultPivotX
+import androidx.ui.core.vectorgraphics.DefaultPivotY
+import androidx.ui.core.vectorgraphics.DefaultRotation
+import androidx.ui.core.vectorgraphics.DefaultScaleX
+import androidx.ui.core.vectorgraphics.DefaultScaleY
+import androidx.ui.core.vectorgraphics.DefaultTranslationX
+import androidx.ui.core.vectorgraphics.DefaultTranslationY
+import androidx.ui.core.vectorgraphics.EmptyBrush
+import androidx.ui.core.vectorgraphics.EmptyPath
+import androidx.ui.core.vectorgraphics.PathNode
+import androidx.ui.core.vectorgraphics.VectorAssetBuilder
+import androidx.ui.core.vectorgraphics.addPathNodes
+import androidx.ui.painting.StrokeCap
+import androidx.ui.painting.StrokeJoin
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParserException
+
+private val LINECAP_BUTT = 0
+private val LINECAP_ROUND = 1
+private val LINECAP_SQUARE = 2
+
+private val LINEJOIN_MITER = 0
+private val LINEJOIN_ROUND = 1
+private val LINEJOIN_BEVEL = 2
+
+private val FILL_TYPE_WINDING = 0
+
+private val SHAPE_CLIP_PATH = "clip-VPath"
+private val SHAPE_GROUP = "group"
+private val SHAPE_PATH = "path"
+
+private fun getStrokeLineCap(id: Int, defValue: StrokeCap = StrokeCap.butt): StrokeCap =
+    when (id) {
+        LINECAP_BUTT -> StrokeCap.butt
+        LINECAP_ROUND -> StrokeCap.round
+        LINECAP_SQUARE -> StrokeCap.square
+        else -> defValue
+    }
+
+private fun getStrokeLineJoin(id: Int, defValue: StrokeJoin = StrokeJoin.miter): StrokeJoin =
+    when (id) {
+        LINEJOIN_MITER -> StrokeJoin.miter
+        LINEJOIN_ROUND -> StrokeJoin.round
+        LINEJOIN_BEVEL -> StrokeJoin.bevel
+        else -> defValue
+    }
+
+internal fun XmlPullParser.isAtEnd(): Boolean =
+    eventType == XmlPullParser.END_DOCUMENT ||
+            (depth < 1 && eventType == XmlPullParser.END_TAG)
+
+internal fun XmlPullParser.parseCurrentVectorNode(
+    res: Resources,
+    attrs: AttributeSet,
+    theme: Resources.Theme? = null,
+    builder: VectorAssetBuilder
+) {
+    when (eventType) {
+        XmlPullParser.START_TAG -> {
+            when (name) {
+                SHAPE_PATH -> {
+                    parsePath(res, theme, attrs, builder)
+                }
+                SHAPE_CLIP_PATH -> {
+                    // TODO parse clipping paths
+                }
+                SHAPE_GROUP -> {
+                    parseGroup(res, theme, attrs, builder)
+                }
+            }
+        }
+        XmlPullParser.END_TAG -> {
+            if (SHAPE_GROUP == name) {
+                builder.popGroup()
+            }
+        }
+    }
+}
+
+/**
+ * Helper method to seek to the first tag within the VectorDrawable xml asset
+ */
+@Throws(XmlPullParserException::class)
+internal fun XmlPullParser.seekToStartTag(): XmlPullParser {
+    var type = next()
+    while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
+        // Empty loop
+        type = next()
+    }
+    if (type != XmlPullParser.START_TAG) {
+        throw XmlPullParserException("No start tag found")
+    }
+    return this
+}
+
+@SuppressWarnings("RestrictedApi")
+internal fun XmlPullParser.createVectorImageBuilder(
+    res: Resources,
+    theme: Resources.Theme?,
+    attrs: AttributeSet
+): VectorAssetBuilder {
+    val vectorAttrs = TypedArrayUtils.obtainAttributes(
+        res,
+        theme,
+        attrs,
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_TYPE_ARRAY
+    )
+
+    // TODO (njawad) handle mirroring here
+//        state.mAutoMirrored = TypedArrayUtils.getNamedBoolean(a, parser, "autoMirrored",
+//                AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_AUTO_MIRRORED, state.mAutoMirrored)
+
+    val viewportWidth = TypedArrayUtils.getNamedFloat(
+        vectorAttrs,
+        this,
+        "viewportWidth",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_WIDTH,
+        0.0f
+    )
+
+    val viewportHeight = TypedArrayUtils.getNamedFloat(
+        vectorAttrs,
+        this,
+        "viewportHeight",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_HEIGHT,
+        0.0f
+    )
+
+    if (viewportWidth <= 0) {
+        throw XmlPullParserException(
+            vectorAttrs.getPositionDescription() +
+                    "<VectorGraphic> tag requires viewportWidth > 0"
+        )
+    } else if (viewportHeight <= 0) {
+        throw XmlPullParserException(
+            vectorAttrs.getPositionDescription() +
+                    "<VectorGraphic> tag requires viewportHeight > 0"
+        )
+    }
+
+    val defaultWidth = vectorAttrs.getDimension(
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_WIDTH, 0.0f
+    )
+    val defaultHeight = vectorAttrs.getDimension(
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_HEIGHT, 0.0f
+    )
+
+    vectorAttrs.recycle()
+
+    return VectorAssetBuilder(
+        defaultWidth = Px(defaultWidth),
+        defaultHeight = Px(defaultHeight),
+        viewportWidth = viewportWidth,
+        viewportHeight = viewportHeight
+    )
+}
+
+@Throws(IllegalArgumentException::class)
+@SuppressWarnings("RestrictedApi")
+internal fun XmlPullParser.parsePath(
+    res: Resources,
+    theme: Resources.Theme?,
+    attrs: AttributeSet,
+    builder: VectorAssetBuilder
+) {
+    val a = TypedArrayUtils.obtainAttributes(
+        res,
+        theme,
+        attrs,
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH
+    )
+
+    val hasPathData = TypedArrayUtils.hasAttribute(this, "pathData")
+    if (!hasPathData) {
+        // If there is no pathData in the VPath tag, then this is an empty VPath,
+        // nothing need to be drawn.
+        throw IllegalArgumentException("No path data available")
+    }
+
+    val name: String = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_NAME) ?: ""
+
+    val pathStr = a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_PATH_DATA)
+
+    val pathData: Array<PathNode> = addPathNodes(pathStr)
+
+    val fillColor = TypedArrayUtils.getNamedComplexColor(
+        a,
+        this,
+        theme,
+        "fillColor",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_COLOR, 0
+    )
+    val fillAlpha = TypedArrayUtils.getNamedFloat(
+        a,
+        this,
+        "fillAlpha",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_ALPHA, 1.0f
+    )
+    val lineCap = TypedArrayUtils.getNamedInt(
+        a,
+        this,
+        "strokeLineCap",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_CAP, -1
+    )
+    val strokeLineCap = getStrokeLineCap(lineCap, StrokeCap.butt)
+    val lineJoin = TypedArrayUtils.getNamedInt(
+        a,
+        this,
+        "strokeLineJoin",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_JOIN, -1
+    )
+    val strokeLineJoin = getStrokeLineJoin(lineJoin, StrokeJoin.bevel)
+    val strokeMiterLimit = TypedArrayUtils.getNamedFloat(
+        a,
+        this,
+        "strokeMiterLimit",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_MITER_LIMIT,
+        1.0f
+    )
+    val strokeColor = TypedArrayUtils.getNamedComplexColor(
+        a,
+        this,
+        theme,
+        "strokeColor",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_COLOR, 0
+    )
+    val strokeAlpha = TypedArrayUtils.getNamedFloat(
+        a,
+        this,
+        "strokeAlpha",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_ALPHA, 1.0f
+    )
+    val strokeLineWidth = TypedArrayUtils.getNamedFloat(
+        a,
+        this,
+        "strokeWidth",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_WIDTH, 1.0f
+    )
+
+    // TODO (njawad) handle trim paths + fill rule
+//    val trimPathEnd = TypedArrayUtils.getNamedFloat(
+//        a, this, "trimPathEnd",
+//        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_END, 1.0f
+//    )
+//    val trimPathOffset = TypedArrayUtils.getNamedFloat(
+//        a, this, "trimPathOffset",
+//        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_OFFSET,
+//        0.0f
+//    )
+//    val trimPathStart = TypedArrayUtils.getNamedFloat(
+//        a, this, "trimPathStart",
+//        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_START,
+//        0.0f
+//    )
+//    val fillRule = TypedArrayUtils.getNamedInt(
+//        a, this, "fillType",
+//        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_FILLTYPE,
+//        FILL_TYPE_WINDING
+//    )
+
+    a.recycle()
+
+    @Suppress("IMPLICIT_CAST_TO_ANY")
+    val fillBrush = if (fillColor.willDraw()) fillColor.color else EmptyBrush
+
+    @Suppress("IMPLICIT_CAST_TO_ANY")
+    val strokeBrush = if (strokeColor.willDraw()) strokeColor.color else EmptyBrush
+
+    builder.addPath(
+        pathData,
+        name,
+        fillBrush,
+        fillAlpha,
+        strokeBrush,
+        strokeAlpha,
+        strokeLineWidth,
+        strokeLineCap,
+        strokeLineJoin,
+        strokeMiterLimit)
+}
+
+@SuppressWarnings("RestrictedApi")
+internal fun XmlPullParser.parseGroup(
+    res: Resources,
+    theme: Resources.Theme?,
+    attrs: AttributeSet,
+    builder: VectorAssetBuilder
+) {
+    val a = TypedArrayUtils.obtainAttributes(
+        res,
+        theme,
+        attrs,
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP
+    )
+
+    // Account for any configuration changes.
+    // mChangingConfigurations |= Utils.getChangingConfigurations(a);
+
+    // Extract the theme attributes, if any.
+    // mThemeAttrs = null // TODO TINT THEME Not supported yet a.extractThemeAttrs();
+
+    // This is added in API 11
+    val rotate = TypedArrayUtils.getNamedFloat(
+        a,
+        this,
+        "rotation",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_ROTATION,
+        DefaultRotation
+    )
+
+    val pivotX = a.getFloat(
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_X,
+        DefaultPivotX
+    )
+    val pivotY = a.getFloat(
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_Y,
+        DefaultPivotY
+    )
+
+    // This is added in API 11
+    val scaleX = TypedArrayUtils.getNamedFloat(
+        a,
+        this,
+        "scaleX",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_X,
+        DefaultScaleX
+    )
+
+    // This is added in API 11
+    val scaleY = TypedArrayUtils.getNamedFloat(
+        a,
+        this,
+        "scaleY",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_Y,
+        DefaultScaleY
+    )
+
+    val translateX = TypedArrayUtils.getNamedFloat(
+        a,
+        this,
+        "translationX",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_X,
+        DefaultTranslationX
+    )
+    val translateY = TypedArrayUtils.getNamedFloat(
+        a,
+        this,
+        "translationY",
+        AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_Y,
+        DefaultTranslationY
+    )
+
+    val name: String =
+        a.getString(AndroidVectorResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_NAME) ?: ""
+
+    // TODO parse clip path
+    val clipPathData = EmptyPath
+
+    a.recycle()
+
+    builder.pushGroup(
+        name,
+        rotate,
+        scaleX,
+        scaleY,
+        translateX,
+        translateY,
+        pivotX,
+        pivotY,
+        clipPathData
+    )
+}
diff --git a/ui/ui-framework/src/test/java/androidx/ui/core/InputFieldDelegateTest.kt b/ui/ui-framework/src/test/java/androidx/ui/core/TextFieldDelegateTest.kt
similarity index 87%
rename from ui/ui-framework/src/test/java/androidx/ui/core/InputFieldDelegateTest.kt
rename to ui/ui-framework/src/test/java/androidx/ui/core/TextFieldDelegateTest.kt
index f934c88..4657c23 100644
--- a/ui/ui-framework/src/test/java/androidx/ui/core/InputFieldDelegateTest.kt
+++ b/ui/ui-framework/src/test/java/androidx/ui/core/TextFieldDelegateTest.kt
@@ -21,7 +21,7 @@
 import androidx.ui.input.CommitTextEditOp
 import androidx.ui.input.EditOperation
 import androidx.ui.input.EditProcessor
-import androidx.ui.input.EditorState
+import androidx.ui.input.EditorModel
 import androidx.ui.input.FinishComposingTextEditOp
 import androidx.ui.input.ImeAction
 import androidx.ui.input.KeyboardType
@@ -49,12 +49,12 @@
 import org.junit.runners.JUnit4
 
 @RunWith(JUnit4::class)
-class InputFieldDelegateTest {
+class TextFieldDelegateTest {
 
     private lateinit var canvas: Canvas
     private lateinit var painter: TextPainter
     private lateinit var processor: EditProcessor
-    private lateinit var onValueChange: (EditorState) -> Unit
+    private lateinit var onValueChange: (EditorModel) -> Unit
     private lateinit var onEditorActionPerformed: (Any) -> Unit
     private lateinit var textInputService: TextInputService
     private lateinit var layoutCoordinates: LayoutCoordinates
@@ -106,10 +106,10 @@
         val selection = TextRange(0, 1)
         val selectionColor = Color.Blue
 
-        InputFieldDelegate.draw(
+        TextFieldDelegate.draw(
             canvas = canvas,
             textPainter = painter,
-            value = EditorState(text = "Hello, World", selection = selection),
+            value = EditorModel(text = "Hello, World", selection = selection),
             editorStyle = EditorStyle(selectionColor = selectionColor),
             hasFocus = true,
             offsetMap = identityOffsetMap
@@ -126,10 +126,10 @@
     fun draw_cursor_test() {
         val cursor = TextRange(1, 1)
 
-        InputFieldDelegate.draw(
+        TextFieldDelegate.draw(
             canvas = canvas,
             textPainter = painter,
-            value = EditorState(text = "Hello, World", selection = cursor),
+            value = EditorModel(text = "Hello, World", selection = cursor),
             editorStyle = EditorStyle(),
             hasFocus = true,
             offsetMap = identityOffsetMap
@@ -144,10 +144,10 @@
     fun dont_draw_cursor_test() {
         val cursor = TextRange(1, 1)
 
-        InputFieldDelegate.draw(
+        TextFieldDelegate.draw(
             canvas = canvas,
             textPainter = painter,
-            value = EditorState(text = "Hello, World", selection = cursor),
+            value = EditorModel(text = "Hello, World", selection = cursor),
             editorStyle = EditorStyle(),
             hasFocus = false,
             offsetMap = identityOffsetMap
@@ -165,10 +165,10 @@
 
         val cursor = TextRange(1, 1)
 
-        InputFieldDelegate.draw(
+        TextFieldDelegate.draw(
             canvas = canvas,
             textPainter = painter,
-            value = EditorState(text = "Hello, World", selection = cursor,
+            value = EditorModel(text = "Hello, World", selection = cursor,
                 composition = composition),
             editorStyle = EditorStyle(compositionColor = compositionColor),
             hasFocus = true,
@@ -184,11 +184,11 @@
     @Test
     fun test_on_edit_command() {
         val ops = listOf(CommitTextEditOp("Hello, World", 1))
-        val dummyEditorState = EditorState(text = "Hello, World", selection = TextRange(1, 1))
+        val dummyEditorState = EditorModel(text = "Hello, World", selection = TextRange(1, 1))
 
         whenever(processor.onEditCommands(ops)).thenReturn(dummyEditorState)
 
-        InputFieldDelegate.onEditCommand(ops, processor, onValueChange)
+        TextFieldDelegate.onEditCommand(ops, processor, onValueChange)
 
         verify(onValueChange, times(1)).invoke(eq(dummyEditorState))
     }
@@ -197,7 +197,7 @@
     fun test_on_release() {
         val position = PxPosition(100.px, 200.px)
         val offset = 10
-        val dummyEditorState = EditorState(text = "Hello, World", selection = TextRange(1, 1))
+        val dummyEditorState = EditorModel(text = "Hello, World", selection = TextRange(1, 1))
 
         whenever(painter.getOffsetForPosition(position)).thenReturn(offset)
 
@@ -205,7 +205,7 @@
 
         whenever(processor.onEditCommands(captor.capture())).thenReturn(dummyEditorState)
 
-        InputFieldDelegate.onRelease(
+        TextFieldDelegate.onRelease(
             position,
             painter,
             processor,
@@ -227,7 +227,7 @@
         val offset = 10
 
         whenever(painter.getOffsetForPosition(position)).thenReturn(offset)
-        InputFieldDelegate.onRelease(
+        TextFieldDelegate.onRelease(
             position,
             painter,
             processor,
@@ -244,10 +244,10 @@
     fun test_draw_order() {
         val canvas: Canvas = mock()
 
-        InputFieldDelegate.draw(
+        TextFieldDelegate.draw(
             canvas = canvas,
             textPainter = painter,
-            value = EditorState(
+            value = EditorModel(
                 text = "Hello, World", selection = TextRange(1, 1),
                 composition = TextRange(1, 3)
             ),
@@ -264,8 +264,8 @@
 
     @Test
     fun on_focus() {
-        val dummyEditorState = EditorState(text = "Hello, World", selection = TextRange(1, 1))
-        InputFieldDelegate.onFocus(textInputService, dummyEditorState, processor,
+        val dummyEditorState = EditorModel(text = "Hello, World", selection = TextRange(1, 1))
+        TextFieldDelegate.onFocus(textInputService, dummyEditorState, processor,
             KeyboardType.Text, ImeAction.Unspecified, onValueChange, onEditorActionPerformed)
         verify(textInputService).startInput(
             eq(dummyEditorState),
@@ -280,9 +280,9 @@
     fun on_blur() {
         val captor = argumentCaptor<List<EditOperation>>()
 
-        whenever(processor.onEditCommands(captor.capture())).thenReturn(EditorState())
+        whenever(processor.onEditCommands(captor.capture())).thenReturn(EditorModel())
 
-        InputFieldDelegate.onBlur(textInputService, processor, onValueChange)
+        TextFieldDelegate.onBlur(textInputService, processor, onValueChange)
 
         assertEquals(1, captor.allValues.size)
         assertEquals(1, captor.firstValue.size)
@@ -296,8 +296,8 @@
         whenever(painter.getBoundingBox(any())).thenReturn(dummyRect)
         val dummyPoint = PxPosition(5.px, 6.px)
         whenever(layoutCoordinates.localToRoot(any())).thenReturn(dummyPoint)
-        val dummyEditorState = EditorState(text = "Hello, World", selection = TextRange(1, 1))
-        InputFieldDelegate.notifyFocusedRect(
+        val dummyEditorState = EditorModel(text = "Hello, World", selection = TextRange(1, 1))
+        TextFieldDelegate.notifyFocusedRect(
             dummyEditorState,
             painter,
             layoutCoordinates,
@@ -310,8 +310,8 @@
 
     @Test
     fun notify_focused_rect_without_focus() {
-        val dummyEditorState = EditorState(text = "Hello, World", selection = TextRange(1, 1))
-        InputFieldDelegate.notifyFocusedRect(
+        val dummyEditorState = EditorModel(text = "Hello, World", selection = TextRange(1, 1))
+        TextFieldDelegate.notifyFocusedRect(
             dummyEditorState,
             painter,
             layoutCoordinates,
@@ -328,8 +328,8 @@
         whenever(painter.getBoundingBox(any())).thenReturn(dummyRect)
         val dummyPoint = PxPosition(5.px, 6.px)
         whenever(layoutCoordinates.localToRoot(any())).thenReturn(dummyPoint)
-        val dummyEditorState = EditorState(text = "Hello, World", selection = TextRange(12, 12))
-        InputFieldDelegate.notifyFocusedRect(
+        val dummyEditorState = EditorModel(text = "Hello, World", selection = TextRange(12, 12))
+        TextFieldDelegate.notifyFocusedRect(
             dummyEditorState,
             painter,
             layoutCoordinates,
@@ -346,8 +346,8 @@
         whenever(painter.preferredLineHeight).thenReturn(dummyHeight)
         val dummyPoint = PxPosition(5.px, 6.px)
         whenever(layoutCoordinates.localToRoot(any())).thenReturn(dummyPoint)
-        val dummyEditorState = EditorState(text = "", selection = TextRange(0, 0))
-        InputFieldDelegate.notifyFocusedRect(
+        val dummyEditorState = EditorModel(text = "", selection = TextRange(0, 0))
+        TextFieldDelegate.notifyFocusedRect(
             dummyEditorState,
             painter,
             layoutCoordinates,
@@ -375,7 +375,7 @@
         whenever(painter.resourceLoader).thenReturn(mock())
         whenever(painter.height).thenReturn(512.0f)
 
-        val res = InputFieldDelegate.layout(painter, constraints)
+        val res = TextFieldDelegate.layout(painter, constraints)
         assertEquals(1024.px.round(), res.first)
         assertEquals(512.px.round(), res.second)
 
@@ -387,10 +387,10 @@
         val selection = TextRange(1, 3)
         val selectionColor = Color.Blue
 
-        InputFieldDelegate.draw(
+        TextFieldDelegate.draw(
             canvas = canvas,
             textPainter = painter,
-            value = EditorState(text = "Hello, World", selection = selection),
+            value = EditorModel(text = "Hello, World", selection = selection),
             editorStyle = EditorStyle(selectionColor = selectionColor),
             hasFocus = true,
             offsetMap = skippingOffsetMap
@@ -410,11 +410,11 @@
     fun check_notify_rect_uses_offset_map() {
         val dummyRect = Rect(0f, 1f, 2f, 3f)
         val dummyPoint = PxPosition(5.px, 6.px)
-        val dummyEditorState = EditorState(text = "Hello, World", selection = TextRange(1, 3))
+        val dummyEditorState = EditorModel(text = "Hello, World", selection = TextRange(1, 3))
         whenever(painter.getBoundingBox(any())).thenReturn(dummyRect)
         whenever(layoutCoordinates.localToRoot(any())).thenReturn(dummyPoint)
 
-        InputFieldDelegate.notifyFocusedRect(
+        TextFieldDelegate.notifyFocusedRect(
             dummyEditorState,
             painter,
             layoutCoordinates,
@@ -430,7 +430,7 @@
     fun check_on_release_uses_offset_map() {
         val position = PxPosition(100.px, 200.px)
         val offset = 10
-        val dummyEditorState = EditorState(text = "Hello, World", selection = TextRange(1, 1))
+        val dummyEditorState = EditorModel(text = "Hello, World", selection = TextRange(1, 1))
 
         whenever(painter.getOffsetForPosition(position)).thenReturn(offset)
 
@@ -438,7 +438,7 @@
 
         whenever(processor.onEditCommands(captor.capture())).thenReturn(dummyEditorState)
 
-        InputFieldDelegate.onRelease(
+        TextFieldDelegate.onRelease(
             position,
             painter,
             processor,
@@ -459,8 +459,8 @@
 
     @Test
     fun use_identity_mapping_if_visual_transformation_is_null() {
-        val (visualText, offsetMap) = InputFieldDelegate.applyVisualFilter(
-            EditorState(text = "Hello, World"),
+        val (visualText, offsetMap) = TextFieldDelegate.applyVisualFilter(
+            EditorModel(text = "Hello, World"),
             null)
 
         assertEquals("Hello, World", visualText.text)
diff --git a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt
index 52c2f1c..568e0a2 100644
--- a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt
+++ b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/ComplexLayoutDemos.kt
@@ -227,7 +227,7 @@
 }
 
 @Composable
-fun SingleCompositionRow(@Children children: @Composable() () -> Unit) {
+fun SingleCompositionRow(children: @Composable() () -> Unit) {
     Layout(layoutBlock = { measurables, constraints ->
         val placeables = measurables.map {
             it.measure(constraints.copy(minWidth = 0.ipx, maxWidth = IntPx.Infinity))
@@ -246,7 +246,7 @@
 }
 
 @Composable
-fun SingleCompositionColumn(@Children children: @Composable() () -> Unit) {
+fun SingleCompositionColumn(children: @Composable() () -> Unit) {
     Layout(layoutBlock = { measurables, constraints ->
         val placeables = measurables.map {
             it.measure(constraints.copy(minHeight = 0.ipx, maxHeight = IntPx.Infinity))
@@ -306,7 +306,7 @@
 }
 
 @Composable
-fun SingleCompositionRowWithIntrinsics(@Children children: @Composable() () -> Unit) {
+fun SingleCompositionRowWithIntrinsics(children: @Composable() () -> Unit) {
     ComplexLayout(children = children, block = {
         layout { measurables, constraints ->
             val placeables = measurables.map { measurable ->
diff --git a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt
index 1b94f79..c42418b 100644
--- a/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt
+++ b/ui/ui-layout/integration-tests/layout-demos/src/main/java/androidx/ui/layout/demos/LayoutActivity.kt
@@ -53,7 +53,7 @@
     width: Dp? = null,
     height: Dp? = null,
     color: Color,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     Wrap {
         DrawRectangle(color = color)
diff --git a/ui/ui-layout/src/androidTest/AndroidManifest.xml b/ui/ui-layout/src/androidTest/AndroidManifest.xml
index 463edf8..b1e8fb7 100644
--- a/ui/ui-layout/src/androidTest/AndroidManifest.xml
+++ b/ui/ui-layout/src/androidTest/AndroidManifest.xml
@@ -17,7 +17,12 @@
     xmlns:tools="http://schemas.android.com/tools"
     package="androidx.ui.layout">
 
+    <!--
+      ~ Important: disable debuggable for accurate performance results
+      ~ requestLegacyExternalStorage to enable legacy JSON reporting when targeting Q
+      -->
     <application
+        android:requestLegacyExternalStorage="true"
         android:debuggable="false"
         tools:ignore="HardcodedDebugMode"
         tools:replace="android:debuggable">
diff --git a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlexTest.kt b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlexTest.kt
index d68feb4..6221fed 100644
--- a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlexTest.kt
+++ b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/FlexTest.kt
@@ -145,9 +145,12 @@
         val root = findAndroidCraneView()
         waitForDraw(root)
 
-        assertEquals(PxSize(root.width.px / 3, childrenHeight.toPx()), childSize[0])
         assertEquals(
-            PxSize(root.width.px * 2 / 3, (heightDp.toPx() * 2).round().toPx()),
+            PxSize((root.width.px / 3).round().toPx(), childrenHeight.toPx()),
+            childSize[0]
+        )
+        assertEquals(
+            PxSize((root.width.px * 2 / 3).round().toPx(), (heightDp.toPx() * 2).round().toPx()),
             childSize[1]
         )
         assertEquals(
diff --git a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/IntrinsicTest.kt b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/IntrinsicTest.kt
index dd375d36..556d6b4 100644
--- a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/IntrinsicTest.kt
+++ b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/IntrinsicTest.kt
@@ -500,7 +500,7 @@
     maxIntrinsicWidth: Dp,
     minIntrinsicHeight: Dp,
     height: Dp, maxIntrinsicHeight: Dp,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     ComplexLayout(children) {
         layout { _, constraints ->
diff --git a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/LayoutTest.kt b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/LayoutTest.kt
index 0042948..ea2810b 100644
--- a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/LayoutTest.kt
+++ b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/LayoutTest.kt
@@ -70,7 +70,7 @@
         activityTestRule.runOnUiThread(runnable)
     }
 
-    internal fun show(@Children composable: @Composable() () -> Unit) {
+    internal fun show(composable: @Composable() () -> Unit) {
         val runnable: Runnable = object : Runnable {
             override fun run() {
                 activity.setContent(composable)
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Align.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Align.kt
index 07144eb..888706f 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Align.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Align.kt
@@ -69,7 +69,7 @@
  * For a widget that does alignment and tries to be the same size as its child, see [Wrap].
  */
 @Composable
-fun Align(alignment: Alignment, @Children children: @Composable() () -> Unit) {
+fun Align(alignment: Alignment, children: @Composable() () -> Unit) {
     Layout(layoutBlock = { measurables, constraints ->
         val measurable = measurables.firstOrNull()
         // The child cannot be larger than our max constraints, but we ignore min constraints.
@@ -112,6 +112,6 @@
  * }
  */
 @Composable
-fun Center(@Children children: @Composable() () -> Unit) {
+fun Center(children: @Composable() () -> Unit) {
     Align(alignment = Alignment.Center, children = children)
 }
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/AspectRatio.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/AspectRatio.kt
index 8117ad6..2856b4b 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/AspectRatio.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/AspectRatio.kt
@@ -51,7 +51,7 @@
 @Composable
 fun AspectRatio(
     aspectRatio: Float,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     Layout(children) { measurables, constraints ->
         val size = listOf(
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/ConstrainedBox.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/ConstrainedBox.kt
index e30c9b3..7ad2633 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/ConstrainedBox.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/ConstrainedBox.kt
@@ -38,7 +38,7 @@
 @Composable
 fun ConstrainedBox(
     constraints: DpConstraints,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     ComplexLayout(children) {
         layout { measurables, incomingConstraints ->
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Container.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Container.kt
index 9027e93..dc48a4b 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Container.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Container.kt
@@ -61,7 +61,7 @@
     constraints: DpConstraints = DpConstraints(),
     width: Dp? = null,
     height: Dp? = null,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     trace("UI:Container") {
         Layout(children = children, layoutBlock = { measurables, incomingConstraints ->
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
index 4860313..21a9a37 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
@@ -178,7 +178,7 @@
     mainAxisSize: FlexSize = FlexSize.Max,
     crossAxisAlignment: CrossAxisAlignment = CrossAxisAlignment.Center,
     crossAxisSize: FlexSize = FlexSize.Min,
-    @Children block: @Composable() () -> Unit
+    block: @Composable() () -> Unit
 ) {
     FlexRow(
         mainAxisAlignment = mainAxisAlignment,
@@ -214,7 +214,7 @@
     mainAxisSize: FlexSize = FlexSize.Max,
     crossAxisAlignment: CrossAxisAlignment = CrossAxisAlignment.Center,
     crossAxisSize: FlexSize = FlexSize.Min,
-    @Children block: @Composable() () -> Unit
+    block: @Composable() () -> Unit
 ) {
     FlexColumn(
         mainAxisAlignment = mainAxisAlignment,
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
index 58b8205..3bd2915 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
@@ -42,7 +42,7 @@
  * the [ConstrainedBox]s to use the same width.
  */
 @Composable
-fun MinIntrinsicWidth(@Children children: @Composable() () -> Unit) {
+fun MinIntrinsicWidth(children: @Composable() () -> Unit) {
     ComplexLayout(children) {
         layout { measurables, constraints ->
             val measurable = measurables.firstOrNull()
@@ -87,7 +87,7 @@
  * the divider to use the same height.
  */
 @Composable
-fun MinIntrinsicHeight(@Children children: @Composable() () -> Unit) {
+fun MinIntrinsicHeight(children: @Composable() () -> Unit) {
     ComplexLayout(children) {
         layout { measurables, constraints ->
             val measurable = measurables.firstOrNull()
@@ -132,7 +132,7 @@
  * The sample is a layout containing three widgets having the same width as the widest one.
  */
 @Composable
-fun MaxIntrinsicWidth(@Children children: @Composable() () -> Unit) {
+fun MaxIntrinsicWidth(children: @Composable() () -> Unit) {
     ComplexLayout(children) {
         layout { measurables, constraints ->
             val measurable = measurables.firstOrNull()
@@ -177,7 +177,7 @@
  * and the divider to use the same height.
  */
 @Composable
-fun MaxIntrinsicHeight(@Children children: @Composable() () -> Unit) {
+fun MaxIntrinsicHeight(children: @Composable() () -> Unit) {
     ComplexLayout(children) {
         layout { measurables, constraints ->
             val measurable = measurables.firstOrNull()
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Padding.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Padding.kt
index 8e8bbe45..79225a6 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Padding.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Padding.kt
@@ -53,7 +53,7 @@
 @Composable
 fun Padding(
     padding: EdgeInsets,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     Layout(layoutBlock = { measurables, constraints ->
         val measurable = measurables.firstOrNull()
@@ -98,7 +98,7 @@
     top: Dp = 0.dp,
     right: Dp = 0.dp,
     bottom: Dp = 0.dp,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     Padding(
         padding = EdgeInsets(left = left, top = top, right = right, bottom = bottom),
@@ -121,7 +121,7 @@
 @Composable
 fun Padding(
     padding: Dp,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     Padding(padding = EdgeInsets(padding), children = children)
 }
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Scroller.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Scroller.kt
index 69bfe04..677be31 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Scroller.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Scroller.kt
@@ -52,7 +52,7 @@
 private fun VerticalDragGestureDetector(
     max: Px = Px.Infinity,
     offsetChange: (Px) -> Unit,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val offset = +state { 0.px }
     DragGestureDetector(
@@ -105,7 +105,7 @@
     onScrollChanged: (position: Px, maxPosition: Px) -> Unit = { position, _ ->
         scrollerPosition.position = position
     },
-    @Children child: @Composable() () -> Unit
+    child: @Composable() () -> Unit
 ) {
     val maxPosition = +state { 0.px }
     Layout(children = {
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Wrap.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Wrap.kt
index 5a08cf8..3846114 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Wrap.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Wrap.kt
@@ -35,7 +35,7 @@
  * For a widget that does alignment and tries to be as large as possible, see [Align].
  */
 @Composable
-fun Wrap(alignment: Alignment = Alignment.TopLeft, @Children children: @Composable() () -> Unit) {
+fun Wrap(alignment: Alignment = Alignment.TopLeft, children: @Composable() () -> Unit) {
     Layout(layoutBlock = { measurables, constraints ->
         val measurable = measurables.firstOrNull()
         // The child cannot be larger than our max constraints, but we ignore min constraints.
diff --git a/ui/ui-material/api/1.0.0-alpha01.txt b/ui/ui-material/api/1.0.0-alpha01.txt
index eba63b6..df0d16a 100644
--- a/ui/ui-material/api/1.0.0-alpha01.txt
+++ b/ui/ui-material/api/1.0.0-alpha01.txt
@@ -240,6 +240,14 @@
 
 }
 
+package androidx.ui.material.internal {
+
+  public final class AnchoredControllerByStateKt {
+    ctor public AnchoredControllerByStateKt();
+  }
+
+}
+
 package androidx.ui.material.ripple {
 
   public final class DefaultRippleEffectFactory implements androidx.ui.material.ripple.RippleEffectFactory {
diff --git a/ui/ui-material/api/current.txt b/ui/ui-material/api/current.txt
index eba63b6..df0d16a 100644
--- a/ui/ui-material/api/current.txt
+++ b/ui/ui-material/api/current.txt
@@ -240,6 +240,14 @@
 
 }
 
+package androidx.ui.material.internal {
+
+  public final class AnchoredControllerByStateKt {
+    ctor public AnchoredControllerByStateKt();
+  }
+
+}
+
 package androidx.ui.material.ripple {
 
   public final class DefaultRippleEffectFactory implements androidx.ui.material.ripple.RippleEffectFactory {
diff --git a/ui/ui-material/api/restricted_1.0.0-alpha01.txt b/ui/ui-material/api/restricted_1.0.0-alpha01.txt
index eba63b6..df0d16a 100644
--- a/ui/ui-material/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-material/api/restricted_1.0.0-alpha01.txt
@@ -240,6 +240,14 @@
 
 }
 
+package androidx.ui.material.internal {
+
+  public final class AnchoredControllerByStateKt {
+    ctor public AnchoredControllerByStateKt();
+  }
+
+}
+
 package androidx.ui.material.ripple {
 
   public final class DefaultRippleEffectFactory implements androidx.ui.material.ripple.RippleEffectFactory {
diff --git a/ui/ui-material/api/restricted_current.txt b/ui/ui-material/api/restricted_current.txt
index eba63b6..df0d16a 100644
--- a/ui/ui-material/api/restricted_current.txt
+++ b/ui/ui-material/api/restricted_current.txt
@@ -240,6 +240,14 @@
 
 }
 
+package androidx.ui.material.internal {
+
+  public final class AnchoredControllerByStateKt {
+    ctor public AnchoredControllerByStateKt();
+  }
+
+}
+
 package androidx.ui.material.ripple {
 
   public final class DefaultRippleEffectFactory implements androidx.ui.material.ripple.RippleEffectFactory {
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionControlsActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionControlsActivity.kt
index a0eaee6..ded34be 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionControlsActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionControlsActivity.kt
@@ -157,11 +157,11 @@
             val (checked, onChecked) = +state { false }
             val (checked2, onChecked2) = +state { false }
             val (checked3, onChecked3) = +state { true }
-            val (checked4, onChecked4) = +state { true }
+            val (checked4, _) = +state { true }
             Switch(checked = checked, onCheckedChange = onChecked)
             Switch(checked = checked2, onCheckedChange = onChecked2, color = customColor)
             Switch(checked = checked3, onCheckedChange = onChecked3, color = customColor2)
-            Switch(checked = checked4, onCheckedChange = onChecked4, color = customColor3)
+            Switch(checked = checked4, onCheckedChange = {}, color = customColor3)
         }
     }
 
@@ -177,4 +177,4 @@
             RadioButton(selected = false, color = customColor, onSelect = null)
         }
     }
-}
+}
\ No newline at end of file
diff --git a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/MissingMaterialComponents.kt b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/MissingMaterialComponents.kt
index bc83b65..ff66edf 100644
--- a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/MissingMaterialComponents.kt
+++ b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/MissingMaterialComponents.kt
@@ -31,7 +31,7 @@
  */
 
 @Composable
-fun Scaffold(appBar: @Composable() () -> Unit, @Children children: @Composable() () -> Unit) {
+fun Scaffold(appBar: @Composable() () -> Unit, children: @Composable() () -> Unit) {
     FlexColumn {
         inflexible {
             appBar()
diff --git a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyTheme.kt b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyTheme.kt
index e80124f..f1d3435 100644
--- a/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyTheme.kt
+++ b/ui/ui-material/integration-tests/material-studies/src/main/java/androidx/ui/material/studies/rally/RallyTheme.kt
@@ -36,7 +36,7 @@
 val rallyBlue = Color(0xFF72DEFF.toInt())
 
 @Composable
-fun RallyTheme(@Children children: @Composable() () -> Unit) {
+fun RallyTheme(children: @Composable() () -> Unit) {
     val colors = MaterialColors(
         primary = rallyGreen,
         surface = Color(0xFF26282F.toInt()),
diff --git a/ui/ui-material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt b/ui/ui-material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt
index d43cd7a..67f7537 100644
--- a/ui/ui-material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt
+++ b/ui/ui-material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt
@@ -63,7 +63,7 @@
     private val options = listOf(itemOne, itemTwo, itemThree)
 
     @Composable
-    fun VerticalRadioGroupforTests(@Children children: @Composable() RadioGroupScope.() -> Unit) {
+    fun VerticalRadioGroupforTests(children: @Composable() RadioGroupScope.() -> Unit) {
         RadioGroup {
             Column {
                 children(p1 = this)
diff --git a/ui/ui-material/src/androidTest/java/androidx/ui/material/RippleEffectTest.kt b/ui/ui-material/src/androidTest/java/androidx/ui/material/RippleEffectTest.kt
index d7707a2..173bb9f 100644
--- a/ui/ui-material/src/androidTest/java/androidx/ui/material/RippleEffectTest.kt
+++ b/ui/ui-material/src/androidTest/java/androidx/ui/material/RippleEffectTest.kt
@@ -192,7 +192,7 @@
     private fun RippleCallback(
         onRippleDrawn: (Matrix4) -> Unit = {},
         onDispose: () -> Unit = {},
-        @Children children: @Composable() () -> Unit
+        children: @Composable() () -> Unit
     ) {
         val theme = RippleTheme(testRippleEffect(onRippleDrawn, onDispose)) { Color(0) }
         CurrentRippleTheme.Provider(value = theme, children = children)
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/Button.kt b/ui/ui-material/src/main/java/androidx/ui/material/Button.kt
index 9ece28d..5137a35 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/Button.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/Button.kt
@@ -74,7 +74,7 @@
     color: Color = +themeColor { primary },
     border: Border? = null,
     elevation: Dp = 0.dp,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val textStyle = +themeTextStyle { button }
     Surface(shape = shape, color = color, border = border, elevation = elevation) {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/Drawer.kt b/ui/ui-material/src/main/java/androidx/ui/material/Drawer.kt
index 557ee14..547149d8 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/Drawer.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/Drawer.kt
@@ -19,8 +19,10 @@
 import androidx.animation.PhysicsBuilder
 import androidx.compose.Composable
 import androidx.compose.composer
+import androidx.compose.memo
 import androidx.compose.onCommit
 import androidx.compose.unaryPlus
+import androidx.ui.animation.animatedFloat
 import androidx.ui.core.Dp
 import androidx.ui.core.IntPx
 import androidx.ui.core.Layout
@@ -34,9 +36,11 @@
 import androidx.ui.core.withDensity
 import androidx.ui.foundation.Clickable
 import androidx.ui.foundation.ColoredRect
-import androidx.ui.foundation.gestures.AnchorsFlingConfig
-import androidx.ui.foundation.gestures.AnimatedDraggable
+import androidx.ui.foundation.animation.AnchorsFlingConfig
+import androidx.ui.foundation.gestures.Draggable
+import androidx.ui.foundation.animation.AnimatedFloatDragController
 import androidx.ui.foundation.gestures.DragDirection
+import androidx.ui.foundation.gestures.DraggableCallback
 import androidx.ui.layout.Alignment
 import androidx.ui.layout.Container
 import androidx.ui.layout.DpConstraints
@@ -120,32 +124,26 @@
             val maxValue = 0f
             val valueByState = if (drawerState == DrawerState.Opened) maxValue else minValue
 
-            val flingConfig = AnchorsFlingConfig(
-                listOf(minValue, maxValue),
-                onAnimationFinished = { finalValue, cancelled ->
-                    if (!cancelled) {
-                        onStateChange(
-                            if (finalValue <= minValue) DrawerState.Closed else DrawerState.Opened
-                        )
-                    }
-                },
-                animationBuilder = AnimationBuilder
-            )
+            val callback = DraggableCallback(onDragSettled = {
+                onStateChange(if (it <= minValue) DrawerState.Closed else DrawerState.Opened)
+            })
+            val flingConfig = AnchorsFlingConfig(listOf(minValue, maxValue), AnimationBuilder)
+            val controller = +memo { AnimatedFloatDragController(valueByState, flingConfig) }
+            +onCommit(valueByState) {
+                controller.animatedFloat.animateTo(valueByState, AnimationBuilder)
+            }
 
-            AnimatedDraggable(
+            Draggable(
                 dragDirection = DragDirection.Horizontal,
-                startValue = valueByState,
                 minValue = minValue,
                 maxValue = maxValue,
-                flingConfig = flingConfig
-            ) { animatedValue ->
-                +onCommit(valueByState) {
-                    animatedValue.animateTo(valueByState, AnimationBuilder)
-                }
-                val fraction = calculateFraction(minValue, maxValue, animatedValue.value)
+                valueController = controller,
+                callback = callback
+            ) { value ->
+                val fraction = calculateFraction(minValue, maxValue, value)
                 val scrimAlpha = fraction * ScrimDefaultOpacity
                 val dpOffset = +withDensity {
-                    animatedValue.value.toDp()
+                    value.toDp()
                 }
 
                 Stack {
@@ -208,31 +206,30 @@
             )
             val valueByState = if (drawerState == DrawerState.Opened) openedValue else maxValue
             val anchors = listOf(minValue, maxValue, openedValue)
+            val callback = DraggableCallback(onDragSettled = {
+                onStateChange(if (it >= maxValue) DrawerState.Closed else DrawerState.Opened)
+            })
 
-            val onAnimationFinished = { finalValue: Float, cancelled: Boolean ->
-                if (!cancelled) {
-                    onStateChange(
-                        if (finalValue >= maxValue) DrawerState.Closed else DrawerState.Opened
-                    )
-                }
+            val flingConfig = AnchorsFlingConfig(anchors, AnimationBuilder)
+
+            val controller = +memo { AnimatedFloatDragController(valueByState, flingConfig) }
+            +onCommit(valueByState) {
+                controller.animatedFloat.animateTo(valueByState, AnimationBuilder)
             }
 
-            AnimatedDraggable(
+            Draggable(
                 dragDirection = DragDirection.Vertical,
-                startValue = valueByState,
                 minValue = minValue,
                 maxValue = maxValue,
-                flingConfig = AnchorsFlingConfig(anchors, onAnimationFinished, AnimationBuilder)
-            ) { animatedValue ->
-                +onCommit(valueByState) {
-                    animatedValue.animateTo(valueByState, AnimationBuilder)
-                }
+                valueController = controller,
+                callback = callback
+            ) { value ->
                 // as we scroll "from height to 0" backwards, (1 - fraction) will reverse it
                 val fractionToOpened =
-                    1 - max(0f, calculateFraction(openedValue, maxValue, animatedValue.value))
+                    1 - max(0f, calculateFraction(openedValue, maxValue, value))
                 val scrimAlpha = fractionToOpened * ScrimDefaultOpacity
                 val dpOffset = +withDensity {
-                    animatedValue.value.toDp()
+                    value.toDp()
                 }
                 Stack {
                     aligned(Alignment.TopLeft) {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt b/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt
index 3ce7acf..019eb31 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/FloatingActionButton.kt
@@ -64,7 +64,7 @@
     shape: Shape = CircleShape,
     color: Color = +themeColor { primary },
     elevation: Dp = 0.dp, // TODO(Andrey) add the default elevation when it ready b/123215187
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     Button(color = color, onClick = onClick, shape = shape, elevation = elevation) {
         Container(constraints = DpConstraints(minWidth = minSize, minHeight = minSize)) {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/MaterialTheme.kt b/ui/ui-material/src/main/java/androidx/ui/material/MaterialTheme.kt
index 296208b..76ce952 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/MaterialTheme.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/MaterialTheme.kt
@@ -57,7 +57,6 @@
 fun MaterialTheme(
     colors: MaterialColors = MaterialColors(),
     typography: MaterialTypography = MaterialTypography(),
-    @Children
     children: @Composable() () -> Unit
 ) {
     Colors.Provider(value = colors) {
@@ -222,7 +221,7 @@
  * guidelines for descendants
  */
 @Composable
-fun MaterialRippleTheme(@Children children: @Composable() () -> Unit) {
+fun MaterialRippleTheme(children: @Composable() () -> Unit) {
     val materialColors = +ambient(Colors)
     val defaultTheme = +memo {
         RippleTheme(
@@ -287,7 +286,7 @@
  * Applies the default [Shape]s for all the surfaces.
  */
 @Composable
-fun MaterialButtonShapeTheme(@Children children: @Composable() () -> Unit) {
+fun MaterialButtonShapeTheme(children: @Composable() () -> Unit) {
     val value = +withDensity {
         Shapes(
             button = RoundedCornerShape(CornerSizes(4.dp))
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/ProgressIndicator.kt b/ui/ui-material/src/main/java/androidx/ui/material/ProgressIndicator.kt
index a8485a0..e2da5bc 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/ProgressIndicator.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/ProgressIndicator.kt
@@ -282,7 +282,7 @@
 }
 
 @Composable
-private fun CircularIndicatorContainer(@Children children: @Composable() () -> Unit) {
+private fun CircularIndicatorContainer(children: @Composable() () -> Unit) {
     Wrap {
         Padding(CircularIndicatorPadding) {
             Container(width = CircularIndicatorDiameter, height = CircularIndicatorDiameter) {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt b/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt
index c3bbe64..ae0f300 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/RadioButton.kt
@@ -76,7 +76,7 @@
  *         onOptionSelected = { ... })
  */
 @Composable
-fun RadioGroup(@Children children: @Composable RadioGroupScope.() -> Unit) {
+fun RadioGroup(children: @Composable RadioGroupScope.() -> Unit) {
     val scope = +memo { RadioGroupScope() }
     children(p1 = scope)
 }
@@ -142,7 +142,7 @@
     fun RadioGroupItem(
         selected: Boolean,
         onSelect: () -> Unit,
-        @Children children: @Composable() () -> Unit
+        children: @Composable() () -> Unit
     ) {
         Container {
             Ripple(bounded = true) {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/Switch.kt b/ui/ui-material/src/main/java/androidx/ui/material/Switch.kt
index 7654959..da22e0f 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/Switch.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/Switch.kt
@@ -16,27 +16,26 @@
 
 package androidx.ui.material
 
-import androidx.animation.ColorPropKey
-import androidx.animation.FastOutSlowInEasing
-import androidx.animation.FloatPropKey
-import androidx.animation.TransitionSpec
-import androidx.animation.transitionDefinition
+import androidx.animation.TweenBuilder
 import androidx.compose.Composable
 import androidx.compose.composer
-import androidx.compose.memo
 import androidx.compose.unaryPlus
-import androidx.ui.animation.Transition
 import androidx.ui.core.DensityReceiver
 import androidx.ui.core.Draw
 import androidx.ui.core.PxSize
 import androidx.ui.core.dp
+import androidx.ui.core.px
+import androidx.ui.core.withDensity
 import androidx.ui.engine.geometry.Offset
+import androidx.ui.foundation.gestures.DragDirection
+import androidx.ui.foundation.gestures.Draggable
 import androidx.ui.foundation.selection.Toggleable
 import androidx.ui.foundation.selection.ToggleableState
 import androidx.ui.graphics.Color
 import androidx.ui.layout.Container
 import androidx.ui.layout.Padding
 import androidx.ui.layout.Wrap
+import androidx.ui.material.internal.anchoredControllerByState
 import androidx.ui.material.ripple.Ripple
 import androidx.ui.painting.Canvas
 import androidx.ui.painting.Paint
@@ -63,9 +62,7 @@
         Ripple(bounded = false) {
             Toggleable(value = value, onToggle = onCheckedChange?.let { { it(!checked) } }) {
                 Padding(padding = DefaultSwitchPadding) {
-                    Container(width = SwitchWidth, height = SwitchHeight) {
-                        DrawSwitch(checked = checked, checkedThumbColor = color)
-                    }
+                    SwitchImpl(checked, onCheckedChange, color)
                 }
             }
         }
@@ -73,49 +70,58 @@
 }
 
 @Composable
-private fun DrawSwitch(checked: Boolean, checkedThumbColor: Color) {
-    val uncheckedThumbColor = +themeColor { surface }
-    val transDef = +memo(checkedThumbColor, uncheckedThumbColor) {
-        generateTransitionDefinition(checkedThumbColor, uncheckedThumbColor)
+private fun SwitchImpl(checked: Boolean, onCheckedChange: ((Boolean) -> Unit)?, color: Color) {
+    val minBound = 0f
+    val maxBound = +withDensity { ThumbPathLength.toPx().value }
+    val (controller, callback) =
+        +anchoredControllerByState(
+            state = checked,
+            onStateChange = onCheckedChange ?: {},
+            anchorsToState = listOf(minBound to false, maxBound to true),
+            animationBuilder = AnimationBuilder
+        )
+    Draggable(
+        dragDirection = DragDirection.Horizontal,
+        minValue = minBound,
+        maxValue = maxBound,
+        valueController = controller,
+        callback = callback
+    ) { thumbPosition ->
+        Container(width = SwitchWidth, height = SwitchHeight, expanded = true) {
+            DrawSwitch(
+                checked = checked,
+                checkedThumbColor = color,
+                thumbPosition = thumbPosition
+            )
+        }
     }
+}
+
+@Composable
+private fun DrawSwitch(checked: Boolean, checkedThumbColor: Color, thumbPosition: Float) {
+    val thumbColor = if (checked) checkedThumbColor else +themeColor { surface }
     val trackColor = if (checked) {
         checkedThumbColor.copy(alpha = CheckedTrackOpacity)
     } else {
         (+themeColor { onSurface }).copy(alpha = UncheckedTrackOpacity)
     }
-    DrawTrack(color = trackColor)
-    Transition(definition = transDef, toState = checked) { state ->
-        DrawThumb(
-            color = state[ThumbColorProp],
-            relativePosition = state[RelativeThumbTranslationProp]
-        )
-    }
-}
-
-@Composable
-private fun DrawTrack(color: Color) {
     Draw { canvas, parentSize ->
-        drawTrack(canvas, parentSize, color)
-    }
-}
-
-@Composable
-private fun DrawThumb(relativePosition: Float, color: Color) {
-    Draw { canvas, parentSize ->
-        drawThumb(canvas, parentSize, relativePosition, color)
+        drawTrack(canvas, parentSize, trackColor)
+        drawThumb(canvas, parentSize, thumbPosition, thumbColor)
     }
 }
 
 private fun DensityReceiver.drawTrack(
     canvas: Canvas,
     parentSize: PxSize,
-    color: Color
+    trackColor: Color
 ) {
-    val paint = Paint()
-    paint.isAntiAlias = true
-    paint.color = color
-    paint.strokeCap = StrokeCap.round
-    paint.strokeWidth = TrackStrokeWidth.toPx().value
+    val paint = Paint().apply {
+        isAntiAlias = true
+        color = trackColor
+        strokeCap = StrokeCap.round
+        strokeWidth = TrackStrokeWidth.toPx().value
+    }
 
     val strokeRadius = TrackStrokeWidth / 2
     val centerHeight = parentSize.height / 2
@@ -130,50 +136,19 @@
 private fun DensityReceiver.drawThumb(
     canvas: Canvas,
     parentSize: PxSize,
-    relativePosition: Float,
-    color: Color
+    position: Float,
+    thumbColor: Color
 ) {
-    val paint = Paint()
-    paint.isAntiAlias = true
-    paint.color = color
-
-    val centerHeight = parentSize.height / 2
-    val thumbRadius = ThumbDiameter / 2
-    val thumbPathWidth = TrackWidth - ThumbDiameter
-
-    val start = thumbRadius + thumbPathWidth * relativePosition
-    canvas.drawCircle(
-        Offset(start.toPx().value, centerHeight.value),
-        // TODO(malkov): wierd +1 but necessary in order to properly cover track. Investigate
-        thumbRadius.toPx().value + 1,
-        paint
-    )
-}
-
-private val RelativeThumbTranslationProp = FloatPropKey()
-private val ThumbColorProp = ColorPropKey()
-private const val SwitchAnimationDuration = 100
-
-private fun generateTransitionDefinition(checkedColor: Color, uncheckedColor: Color) =
-    transitionDefinition {
-        fun <T> TransitionSpec<Boolean>.switchTween() = tween<T> {
-            duration = SwitchAnimationDuration
-            easing = FastOutSlowInEasing
-        }
-
-        state(false) {
-            this[RelativeThumbTranslationProp] = 0f
-            this[ThumbColorProp] = uncheckedColor
-        }
-        state(true) {
-            this[RelativeThumbTranslationProp] = 1f
-            this[ThumbColorProp] = checkedColor
-        }
-        transition {
-            RelativeThumbTranslationProp using switchTween()
-            ThumbColorProp using switchTween()
-        }
+    val paint = Paint().apply {
+        isAntiAlias = true
+        color = thumbColor
     }
+    val centerHeight = parentSize.height / 2
+    val thumbRadius = (ThumbDiameter / 2).toPx().value
+    val x = position.px.value + thumbRadius
+
+    canvas.drawCircle(Offset(x, centerHeight.value), thumbRadius, paint)
+}
 
 private val CheckedTrackOpacity = 0.54f
 private val UncheckedTrackOpacity = 0.38f
@@ -186,4 +161,7 @@
 // TODO(malkov): clarify this padding for Switch
 private val DefaultSwitchPadding = 2.dp
 private val SwitchWidth = TrackWidth
-private val SwitchHeight = ThumbDiameter
\ No newline at end of file
+private val SwitchHeight = ThumbDiameter
+private val ThumbPathLength = TrackWidth - ThumbDiameter
+
+private val AnimationBuilder = TweenBuilder<Float>().apply { duration = 100 }
\ No newline at end of file
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/internal/AnchoredControllerByState.kt b/ui/ui-material/src/main/java/androidx/ui/material/internal/AnchoredControllerByState.kt
new file mode 100644
index 0000000..c8416a3
--- /dev/null
+++ b/ui/ui-material/src/main/java/androidx/ui/material/internal/AnchoredControllerByState.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.material.internal
+
+import androidx.animation.AnimationBuilder
+import androidx.compose.composer
+import androidx.compose.effectOf
+import androidx.compose.memo
+import androidx.compose.onCommit
+import androidx.compose.state
+import androidx.ui.foundation.gestures.Draggable
+import androidx.ui.foundation.animation.AnchorsFlingConfig
+import androidx.ui.foundation.animation.AnimatedFloatDragController
+import androidx.ui.foundation.gestures.DragValueController
+import androidx.ui.foundation.gestures.DraggableCallback
+
+/**
+ * Effect to create special version if [AnimatedFloatDragController] that maps
+ * float anchors to states
+ *
+ * Alongside with control that [AnimatedFloatDragController] provides, this component ensures that:
+ * 1. The AnimatedFloat value will be in sync with call site state
+ * 2. When the anchor is reached, [onStateChange] will be called with state mapped to this anchor
+ * 3. When the anchor is reached and [onStateChange] with corresponding state is called, but
+ * call site didn't update state to the reached one for some reason,
+ * this component performs rollback to the previous (correct) state.
+ * 4. When new [state] is provided, component will be animated to state's anchor
+ *
+ * @param T type with which state is represented
+ * @param state current state to represent Float value with
+ * @param onStateChange callback to update call site's state
+ * @param anchorsToState pairs of anchors to states to map anchors to state and vise versa
+ * @param animationBuilder animation which will be used for animations
+ * @return pair of controller and callback to pass to [Draggable].
+ */
+// TODO(malkov/tianliu) (figure our how to make it better and make public)
+internal fun <T> anchoredControllerByState(
+    state: T,
+    onStateChange: (T) -> Unit,
+    anchorsToState: List<Pair<Float, T>>,
+    animationBuilder: AnimationBuilder<Float>
+) = effectOf<Pair<DragValueController, DraggableCallback>> {
+    val anchors = +memo(anchorsToState) { anchorsToState.map { it.first } }
+    val currentValue = anchorsToState.firstOrNull { it.second == state }!!.first
+
+    // This state is to force this component to be recomposed and trigger +onCommit below
+    // This is needed to stay in sync with drag state that caller side holds
+    val forceAnimationCheck = +state { true }
+    val callback = DraggableCallback(onDragSettled = { finalValue ->
+        val newState = anchorsToState.firstOrNull { it.first == finalValue }?.second
+        if (newState != null && newState != state) {
+            onStateChange(newState)
+            forceAnimationCheck.value = !forceAnimationCheck.value
+        }
+    })
+
+    val controller = +memo(anchorsToState, animationBuilder) {
+        AnimatedFloatDragController(currentValue, AnchorsFlingConfig(anchors, animationBuilder))
+    }
+    +onCommit(currentValue, forceAnimationCheck.value) {
+        controller.animatedFloat.animateTo(currentValue, animationBuilder)
+    }
+    controller to callback
+}
\ No newline at end of file
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/ripple/Ripple.kt b/ui/ui-material/src/main/java/androidx/ui/material/ripple/Ripple.kt
index 139a554..382e2ab 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/ripple/Ripple.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/ripple/Ripple.kt
@@ -60,7 +60,7 @@
 fun Ripple(
     bounded: Boolean,
     radius: Dp? = null,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val density = +ambientDensity()
     val rippleSurface = +ambientRippleSurface()
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/ripple/RippleSurface.kt b/ui/ui-material/src/main/java/androidx/ui/material/ripple/RippleSurface.kt
index 48d547f..aa0dd8c 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/ripple/RippleSurface.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/ripple/RippleSurface.kt
@@ -87,7 +87,7 @@
 @Composable
 fun RippleSurface(
     color: Color?,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     val owner = +memo { RippleSurfaceOwnerImpl() }
     owner.backgroundColor = color
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/surface/Card.kt b/ui/ui-material/src/main/java/androidx/ui/material/surface/Card.kt
index 52f1f13f..544666b 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/surface/Card.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/surface/Card.kt
@@ -49,7 +49,7 @@
     color: Color = +themeColor { surface },
     border: Border? = null,
     elevation: Dp = 0.dp,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     // TODO(Andrey: This currently adds no logic on top of Surface, I just reserve the name
     // for now. We will see what will be the additional Card specific logic later.
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/surface/Surface.kt b/ui/ui-material/src/main/java/androidx/ui/material/surface/Surface.kt
index 0981820..8a75fb3 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/surface/Surface.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/surface/Surface.kt
@@ -84,7 +84,7 @@
     color: Color = +themeColor { surface },
     border: Border? = null,
     elevation: Dp = 0.dp,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     SurfaceLayout {
         DrawShadow(shape = shape, elevation = elevation)
@@ -110,7 +110,7 @@
  * TODO("Andrey: Should be replaced with some basic layout implementation when we have it")
  */
 @Composable
-private fun SurfaceLayout(@Children children: @Composable() () -> Unit) {
+private fun SurfaceLayout(children: @Composable() () -> Unit) {
     Layout(children = children, layoutBlock = { measurables, constraints ->
         if (measurables.size > 1) {
             throw IllegalStateException("Surface can have only one direct measurable child!")
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/surface/TransparentSurface.kt b/ui/ui-material/src/main/java/androidx/ui/material/surface/TransparentSurface.kt
index 29aea48..f68b95c 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/surface/TransparentSurface.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/surface/TransparentSurface.kt
@@ -38,7 +38,7 @@
 @Composable
 fun TransparentSurface(
     shape: Shape = RectangleShape,
-    @Children children: @Composable() () -> Unit
+    children: @Composable() () -> Unit
 ) {
     Surface(shape = shape, children = children, color = Color.Transparent)
 }
diff --git a/ui/ui-platform/src/main/java/androidx/ui/core/input/TextInputServiceAndroid.kt b/ui/ui-platform/src/main/java/androidx/ui/core/input/TextInputServiceAndroid.kt
index 0989de0..e77e070 100644
--- a/ui/ui-platform/src/main/java/androidx/ui/core/input/TextInputServiceAndroid.kt
+++ b/ui/ui-platform/src/main/java/androidx/ui/core/input/TextInputServiceAndroid.kt
@@ -24,7 +24,7 @@
 import android.view.inputmethod.InputMethodManager
 import androidx.ui.engine.geometry.Rect
 import androidx.ui.input.EditOperation
-import androidx.ui.input.EditorState
+import androidx.ui.input.EditorModel
 import androidx.ui.input.ImeAction
 import androidx.ui.input.InputEventListener
 import androidx.ui.input.KeyboardType
@@ -86,14 +86,14 @@
     fun isEditorFocused(): Boolean = editorHasFocus
 
     override fun startInput(
-        initState: EditorState,
+        initModel: EditorModel,
         keyboardType: KeyboardType,
         imeAction: ImeAction,
         onEditCommand: (List<EditOperation>) -> Unit,
         onImeActionPerformed: (ImeAction) -> Unit
     ) {
         editorHasFocus = true
-        state = initState.toInputState()
+        state = initModel.toInputState()
         this.keyboardType = keyboardType
         this.imeAction = imeAction
         this.onEditCommand = onEditCommand
@@ -118,8 +118,8 @@
         imm.showSoftInput(view, 0)
     }
 
-    override fun onStateUpdated(state: EditorState) {
-        this.state = state.toInputState()
+    override fun onStateUpdated(model: EditorModel) {
+        this.state = model.toInputState()
         ic?.updateInputState(this.state, imm, view)
     }
 
@@ -178,7 +178,7 @@
     }
 }
 
-private fun EditorState.toInputState(): InputState =
+private fun EditorModel.toInputState(): InputState =
     InputState(
         text = text, // TODO(nona): call toString once AnnotatedString is in use.
         selection = selection,
diff --git a/ui/ui-platform/src/test/java/androidx/ui/core/input/TextInputServiceAndroidTest.kt b/ui/ui-platform/src/test/java/androidx/ui/core/input/TextInputServiceAndroidTest.kt
index 575460d..0d32cf82 100644
--- a/ui/ui-platform/src/test/java/androidx/ui/core/input/TextInputServiceAndroidTest.kt
+++ b/ui/ui-platform/src/test/java/androidx/ui/core/input/TextInputServiceAndroidTest.kt
@@ -22,7 +22,7 @@
 import android.view.inputmethod.EditorInfo
 import android.view.inputmethod.InputMethodManager
 import androidx.test.filters.SmallTest
-import androidx.ui.input.EditorState
+import androidx.ui.input.EditorModel
 import androidx.ui.input.ImeAction
 import androidx.ui.input.KeyboardType
 import com.nhaarman.mockitokotlin2.eq
@@ -54,7 +54,7 @@
     @Test
     fun test_fill_editor_info_text() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Text,
             ImeAction.Unspecified,
             onEditCommand = {},
@@ -71,7 +71,7 @@
     @Test
     fun test_fill_editor_info_ascii() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Ascii,
             ImeAction.Unspecified,
             onEditCommand = {},
@@ -89,7 +89,7 @@
     @Test
     fun test_fill_editor_info_number() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Number,
             ImeAction.Unspecified,
             onEditCommand = {},
@@ -106,7 +106,7 @@
     @Test
     fun test_fill_editor_info_phone() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Phone,
             ImeAction.Unspecified,
             onEditCommand = {},
@@ -123,7 +123,7 @@
     @Test
     fun test_fill_editor_info_uri() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Uri,
             ImeAction.Unspecified,
             onEditCommand = {},
@@ -141,7 +141,7 @@
     @Test
     fun test_fill_editor_info_email() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Email,
             ImeAction.Unspecified,
             onEditCommand = {},
@@ -159,7 +159,7 @@
     @Test
     fun test_fill_editor_info_password() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Password,
             ImeAction.Unspecified,
             onEditCommand = {},
@@ -177,7 +177,7 @@
     @Test
     fun test_fill_editor_info_number_password() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.NumberPassword,
             ImeAction.Unspecified,
             onEditCommand = {},
@@ -195,7 +195,7 @@
     @Test
     fun test_fill_editor_info_action_none() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Ascii,
             ImeAction.NoAction,
             onEditCommand = {},
@@ -213,7 +213,7 @@
     @Test
     fun test_fill_editor_info_action_go() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Ascii,
             ImeAction.Go,
             onEditCommand = {},
@@ -231,7 +231,7 @@
     @Test
     fun test_fill_editor_info_action_next() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Ascii,
             ImeAction.Next,
             onEditCommand = {},
@@ -249,7 +249,7 @@
     @Test
     fun test_fill_editor_info_action_previous() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Ascii,
             ImeAction.Previous,
             onEditCommand = {},
@@ -267,7 +267,7 @@
     @Test
     fun test_fill_editor_info_action_search() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Ascii,
             ImeAction.Search,
             onEditCommand = {},
@@ -285,7 +285,7 @@
     @Test
     fun test_fill_editor_info_action_send() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Ascii,
             ImeAction.Send,
             onEditCommand = {},
@@ -303,7 +303,7 @@
     @Test
     fun test_fill_editor_info_action_done() {
         textInputService.startInput(
-            EditorState(""),
+            EditorModel(""),
             KeyboardType.Ascii,
             ImeAction.Done,
             onEditCommand = {},
diff --git a/ui/ui-text/api/1.0.0-alpha01.txt b/ui/ui-text/api/1.0.0-alpha01.txt
index 59a09a0..a37b2f2 100644
--- a/ui/ui-text/api/1.0.0-alpha01.txt
+++ b/ui/ui-text/api/1.0.0-alpha01.txt
@@ -1,8 +1,8 @@
 // Signature format: 3.0
 package androidx.ui.input {
 
-  public final class EditorState {
-    ctor public EditorState(String text, androidx.ui.text.TextRange selection);
+  public final class EditorModel {
+    ctor public EditorModel(String text, androidx.ui.text.TextRange selection);
     method public androidx.ui.text.TextRange? getComposition();
     method public androidx.ui.text.TextRange getSelection();
     method public String getText();
@@ -38,10 +38,12 @@
 package androidx.ui.text {
 
   public final class AnnotatedString {
-    ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+    ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
     method public String component1();
     method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> component2();
-    method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+    method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> component3();
+    method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
+    method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> getParagraphStyles();
     method public String getText();
     method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> getTextStyles();
   }
@@ -76,6 +78,10 @@
     method public String _canonicalizeRegionCode(String regionCode);
   }
 
+  public final class MultiParagraphKt {
+    ctor public MultiParagraphKt();
+  }
+
   public interface Paragraph {
     method public float getBaseline();
     method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
@@ -127,6 +133,7 @@
     method public androidx.ui.text.style.TextAlign? getTextAlign();
     method public androidx.ui.text.style.TextDirection? getTextDirection();
     method public androidx.ui.text.style.TextIndent? getTextIndent();
+    method public androidx.ui.text.ParagraphStyle merge(androidx.ui.text.ParagraphStyle? other = null);
   }
 
   public final class TextPainter {
diff --git a/ui/ui-text/api/api_lint.ignore b/ui/ui-text/api/api_lint.ignore
index 577c67f..26f0fc9 100644
--- a/ui/ui-text/api/api_lint.ignore
+++ b/ui/ui-text/api/api_lint.ignore
@@ -48,12 +48,8 @@
 
 
 KotlinOperator: androidx.ui.text.font.FontFamily#contains(androidx.ui.text.font.Font):
-    Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: androidx.ui.text.font.FontFamily#get(int):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+    Note that adding the `operator` keyword would allow calling this method using operator syntax
 KotlinOperator: androidx.ui.text.font.FontFamilyList#contains(androidx.ui.text.font.FontFamily):
-    Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
-KotlinOperator: androidx.ui.text.font.FontFamilyList#get(int):
-    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+    Note that adding the `operator` keyword would allow calling this method using operator syntax
 KotlinOperator: androidx.ui.text.style.TextDecoration#contains(androidx.ui.text.style.TextDecoration):
-    Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+    Note that adding the `operator` keyword would allow calling this method using operator syntax
diff --git a/ui/ui-text/api/current.txt b/ui/ui-text/api/current.txt
index 59a09a0..a37b2f2 100644
--- a/ui/ui-text/api/current.txt
+++ b/ui/ui-text/api/current.txt
@@ -1,8 +1,8 @@
 // Signature format: 3.0
 package androidx.ui.input {
 
-  public final class EditorState {
-    ctor public EditorState(String text, androidx.ui.text.TextRange selection);
+  public final class EditorModel {
+    ctor public EditorModel(String text, androidx.ui.text.TextRange selection);
     method public androidx.ui.text.TextRange? getComposition();
     method public androidx.ui.text.TextRange getSelection();
     method public String getText();
@@ -38,10 +38,12 @@
 package androidx.ui.text {
 
   public final class AnnotatedString {
-    ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+    ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
     method public String component1();
     method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> component2();
-    method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+    method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> component3();
+    method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
+    method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> getParagraphStyles();
     method public String getText();
     method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> getTextStyles();
   }
@@ -76,6 +78,10 @@
     method public String _canonicalizeRegionCode(String regionCode);
   }
 
+  public final class MultiParagraphKt {
+    ctor public MultiParagraphKt();
+  }
+
   public interface Paragraph {
     method public float getBaseline();
     method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
@@ -127,6 +133,7 @@
     method public androidx.ui.text.style.TextAlign? getTextAlign();
     method public androidx.ui.text.style.TextDirection? getTextDirection();
     method public androidx.ui.text.style.TextIndent? getTextIndent();
+    method public androidx.ui.text.ParagraphStyle merge(androidx.ui.text.ParagraphStyle? other = null);
   }
 
   public final class TextPainter {
diff --git a/ui/ui-text/api/restricted_1.0.0-alpha01.txt b/ui/ui-text/api/restricted_1.0.0-alpha01.txt
index cb841ce..d02f68726 100644
--- a/ui/ui-text/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-text/api/restricted_1.0.0-alpha01.txt
@@ -8,8 +8,8 @@
 
 
 
-  public final class EditorState {
-    ctor public EditorState(String text, androidx.ui.text.TextRange selection);
+  public final class EditorModel {
+    ctor public EditorModel(String text, androidx.ui.text.TextRange selection);
     method public androidx.ui.text.TextRange? getComposition();
     method public androidx.ui.text.TextRange getSelection();
     method public String getText();
@@ -52,10 +52,12 @@
 package androidx.ui.text {
 
   public final class AnnotatedString {
-    ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+    ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
     method public String component1();
     method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> component2();
-    method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+    method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> component3();
+    method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
+    method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> getParagraphStyles();
     method public String getText();
     method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> getTextStyles();
   }
@@ -90,6 +92,10 @@
     method public String _canonicalizeRegionCode(String regionCode);
   }
 
+  public final class MultiParagraphKt {
+    ctor public MultiParagraphKt();
+  }
+
   public interface Paragraph {
     method public float getBaseline();
     method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
@@ -141,6 +147,7 @@
     method public androidx.ui.text.style.TextAlign? getTextAlign();
     method public androidx.ui.text.style.TextDirection? getTextDirection();
     method public androidx.ui.text.style.TextIndent? getTextIndent();
+    method public androidx.ui.text.ParagraphStyle merge(androidx.ui.text.ParagraphStyle? other = null);
   }
 
   public final class TextPainter {
diff --git a/ui/ui-text/api/restricted_current.txt b/ui/ui-text/api/restricted_current.txt
index cb841ce..d02f68726 100644
--- a/ui/ui-text/api/restricted_current.txt
+++ b/ui/ui-text/api/restricted_current.txt
@@ -8,8 +8,8 @@
 
 
 
-  public final class EditorState {
-    ctor public EditorState(String text, androidx.ui.text.TextRange selection);
+  public final class EditorModel {
+    ctor public EditorModel(String text, androidx.ui.text.TextRange selection);
     method public androidx.ui.text.TextRange? getComposition();
     method public androidx.ui.text.TextRange getSelection();
     method public String getText();
@@ -52,10 +52,12 @@
 package androidx.ui.text {
 
   public final class AnnotatedString {
-    ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+    ctor public AnnotatedString(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
     method public String component1();
     method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> component2();
-    method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles);
+    method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> component3();
+    method public androidx.ui.text.AnnotatedString copy(String text, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> textStyles, java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> paragraphStyles);
+    method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.ParagraphStyle>> getParagraphStyles();
     method public String getText();
     method public java.util.List<androidx.ui.text.AnnotatedString.Item<androidx.ui.text.TextStyle>> getTextStyles();
   }
@@ -90,6 +92,10 @@
     method public String _canonicalizeRegionCode(String regionCode);
   }
 
+  public final class MultiParagraphKt {
+    ctor public MultiParagraphKt();
+  }
+
   public interface Paragraph {
     method public float getBaseline();
     method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
@@ -141,6 +147,7 @@
     method public androidx.ui.text.style.TextAlign? getTextAlign();
     method public androidx.ui.text.style.TextDirection? getTextDirection();
     method public androidx.ui.text.style.TextIndent? getTextIndent();
+    method public androidx.ui.text.ParagraphStyle merge(androidx.ui.text.ParagraphStyle? other = null);
   }
 
   public final class TextPainter {
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneInputField.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneInputField.kt
index c71370d..b345b40 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneInputField.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneInputField.kt
@@ -21,9 +21,8 @@
 import androidx.compose.state
 import androidx.compose.unaryPlus
 import androidx.ui.core.EditorStyle
-import androidx.ui.core.InputField
-import androidx.ui.text.TextRange
-import androidx.ui.input.EditorState
+import androidx.ui.core.TextField
+import androidx.ui.input.EditorModel
 import androidx.ui.input.ImeAction
 import androidx.ui.input.KeyboardType
 import androidx.ui.layout.Column
@@ -79,8 +78,8 @@
     keyboardType: KeyboardType = KeyboardType.Text,
     imeAction: ImeAction = ImeAction.Unspecified
 ) {
-    val state = +state { EditorState() }
-    InputField(
+    val state = +state { EditorModel() }
+    TextField(
         value = state.value,
         keyboardType = keyboardType,
         imeAction = imeAction,
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneText.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneText.kt
index faaf76d..a0e9bda 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneText.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneText.kt
@@ -47,6 +47,8 @@
 import androidx.ui.text.style.TextOverflow
 import androidx.ui.core.Sp
 import androidx.ui.core.sp
+import androidx.ui.text.AnnotatedString
+import androidx.ui.text.style.TextIndent
 
 val displayText = "Text Demo"
 val displayTextChinese = "文本演示"
@@ -99,6 +101,16 @@
             TextDemoComposableTextSpan()
             TagLine(tag = "fontSizeScale")
             TextDemoFontSizeScale()
+            TagLine(tag = "multiple paragraphs basic")
+            TextDemoParagraph()
+            TagLine(tag = "multiple paragraphs TextAlign")
+            TextDemoParagraphTextAlign()
+            TagLine(tag = "multiple paragraphs line height")
+            TextDemoParagraphLineHeight()
+            TagLine(tag = "multiple paragraphs TextIndent")
+            TextDemoParagraphIndent()
+            TagLine(tag = "multiple paragraphs TextDirection")
+            TextDemoParagraphTextDirection()
         }
     }
 }
@@ -685,3 +697,131 @@
         }
     }
 }
+
+@Composable
+fun TextDemoParagraph() {
+    val text1 = "paragraph1 paragraph1 paragraph1 paragraph1 paragraph1"
+    val text2 = "paragraph2 paragraph2 paragraph2 paragraph2 paragraph2"
+    Text(
+        text = AnnotatedString(
+            text = text1 + text2,
+            textStyles = listOf(),
+            paragraphStyles = listOf(
+                AnnotatedString.Item(ParagraphStyle(), text1.length, text1.length)
+            )
+        ),
+        style = TextStyle(fontSize = fontSize8)
+    )
+}
+
+@Composable
+fun TextDemoParagraphTextAlign() {
+    var text = ""
+    val paragraphStyles = mutableListOf<AnnotatedString.Item<ParagraphStyle>>()
+    TextAlign.values().map { textAlign ->
+        val str = List(4) { "TextAlign.$textAlign" }.joinToString(" ")
+        val paragraphStyle = ParagraphStyle(textAlign = textAlign)
+        Pair(str, paragraphStyle)
+    }.forEach { (str, paragraphStyle) ->
+        paragraphStyles.add(
+            AnnotatedString.Item(
+                paragraphStyle,
+                text.length,
+                text.length + str.length
+            )
+        )
+        text += str
+    }
+
+    Text(
+        text = AnnotatedString(
+            text = text,
+            textStyles = listOf(),
+            paragraphStyles = paragraphStyles
+        ),
+        style = TextStyle(fontSize = fontSize8)
+    )
+}
+
+@Composable
+fun TextDemoParagraphLineHeight() {
+    val text1 = "LineHeight=1.0f LineHeight=1.0f LineHeight=1.0f LineHeight=1.0f"
+    val text2 = "LineHeight=1.5f LineHeight=1.5f LineHeight=1.5f LineHeight=1.5f"
+    val text3 = "LineHeight=3.0f LineHeight=3.0f LineHeight=3.0f LineHeight=3.0f"
+
+    Text(
+        text = AnnotatedString(
+            text = text1 + text2 + text3,
+            textStyles = listOf(),
+            paragraphStyles = listOf(
+                AnnotatedString.Item(
+                    ParagraphStyle(lineHeight = 1.0f),
+                    0,
+                    text1.length
+                ),
+                AnnotatedString.Item(
+                    ParagraphStyle(lineHeight = 1.5f),
+                    text1.length,
+                    text1.length + text2.length
+                ),
+                AnnotatedString.Item(
+                    ParagraphStyle(lineHeight = 2f),
+                    text1.length + text2.length,
+                    text1.length + text2.length + text3.length
+                )
+            )
+        ),
+        style = TextStyle(fontSize = fontSize8)
+    )
+}
+
+@Composable
+fun TextDemoParagraphIndent() {
+    val text1 = "TextIndent firstLine TextIndent firstLine TextIndent firstLine"
+    val text2 = "TextIndent restLine TextIndent restLine TextIndent restLine"
+
+    Text(
+        text = AnnotatedString(
+            text = text1 + text2,
+            textStyles = listOf(),
+            paragraphStyles = listOf(
+                AnnotatedString.Item(
+                    ParagraphStyle(textIndent = TextIndent(firstLine = 100.px)),
+                    0,
+                    text1.length
+                ),
+                AnnotatedString.Item(
+                    ParagraphStyle(textIndent = TextIndent(restLine = 100.px)),
+                    text1.length,
+                    text1.length + text2.length
+                )
+            )
+        ),
+        style = TextStyle(fontSize = fontSize8)
+    )
+}
+
+@Composable
+fun TextDemoParagraphTextDirection() {
+    val ltrText = "Hello World! Hello World! Hello World! Hello World! Hello World!"
+    val rtlText = "مرحبا بالعالم مرحبا بالعالم مرحبا بالعالم مرحبا بالعالم مرحبا بالعالم"
+    Text(
+        text = AnnotatedString(
+            text = ltrText + rtlText,
+            textStyles = listOf(),
+            paragraphStyles = listOf(
+                AnnotatedString.Item(
+                    ParagraphStyle(textDirection = TextDirection.Ltr),
+                    0,
+                    ltrText.length
+                ),
+                AnnotatedString.Item(
+                    ParagraphStyle(textDirection = TextDirection.Rtl),
+                    ltrText.length,
+                    ltrText.length + rtlText.length
+                )
+            )
+        ),
+        style = TextStyle(fontSize = fontSize8)
+    )
+}
\ No newline at end of file
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneVariousInputField.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneVariousInputField.kt
index 1f46d90..47b3abb 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneVariousInputField.kt
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneVariousInputField.kt
@@ -21,12 +21,16 @@
 import androidx.compose.state
 import androidx.compose.unaryPlus
 import androidx.ui.core.EditorStyle
-import androidx.ui.core.InputField
+import androidx.ui.core.Layout
 import androidx.ui.core.OffsetMap
 import androidx.ui.core.PasswordVisualTransformation
+import androidx.ui.core.Text
+import androidx.ui.core.TextField
 import androidx.ui.core.TransformedText
 import androidx.ui.core.VisualTransformation
-import androidx.ui.input.EditorState
+import androidx.ui.core.ipx
+import androidx.ui.graphics.Color
+import androidx.ui.input.EditorModel
 import androidx.ui.input.ImeAction
 import androidx.ui.input.KeyboardType
 import androidx.ui.layout.Column
@@ -220,6 +224,17 @@
                 keyboardType = KeyboardType.Email,
                 visualTransformation = emailFilter
             )
+
+            TagLine(tag = "Editfield with Hint Text")
+            HintEditText @Composable {
+                Text(
+                    text = "Hint Text",
+                    style = TextStyle(
+                        color = Color(0xFF888888.toInt()),
+                        fontSize = fontSize8
+                    )
+                )
+            }
         }
     }
 }
@@ -228,11 +243,11 @@
 fun VariousEditLine(
     keyboardType: KeyboardType = KeyboardType.Text,
     imeAction: ImeAction = ImeAction.Unspecified,
-    onValueChange: (EditorState, EditorState) -> EditorState = { _, new -> new },
+    onValueChange: (EditorModel, EditorModel) -> EditorModel = { _, new -> new },
     visualTransformation: VisualTransformation
 ) {
-    val state = +state { EditorState() }
-    InputField(
+    val state = +state { EditorModel() }
+    TextField(
         value = state.value,
         keyboardType = keyboardType,
         imeAction = imeAction,
@@ -241,3 +256,32 @@
         editorStyle = EditorStyle(textStyle = TextStyle(fontSize = fontSize8))
     )
 }
+
+@Composable
+fun HintEditText(hintText: @Composable() () -> Unit) {
+    val state = +state { EditorModel() }
+
+    val inputField = @Composable {
+        TextField(
+            value = state.value,
+            onValueChange = { state.value = it },
+            editorStyle = EditorStyle(textStyle = TextStyle(fontSize = fontSize8))
+        )
+    }
+
+    if (state.value.text.isNotEmpty()) {
+        inputField()
+    } else {
+        Layout(
+            childrenArray = arrayOf(inputField, hintText),
+            layoutBlock = { measurable, constraints ->
+                val inputfieldPlacable = measurable[inputField].first().measure(constraints)
+                val hintTextPlacable = measurable[hintText].first().measure(constraints)
+                layout(inputfieldPlacable.width, inputfieldPlacable.height) {
+                    inputfieldPlacable.place(0.ipx, 0.ipx)
+                    hintTextPlacable.place(0.ipx, 0.ipx)
+                }
+            }
+        )
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-text/src/androidTest/java/androidx/ui/text/MultiParagraphIntegrationTest.kt b/ui/ui-text/src/androidTest/java/androidx/ui/text/MultiParagraphIntegrationTest.kt
new file mode 100644
index 0000000..75c21b6
--- /dev/null
+++ b/ui/ui-text/src/androidTest/java/androidx/ui/text/MultiParagraphIntegrationTest.kt
@@ -0,0 +1,1925 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.ui.text
+
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import androidx.test.filters.Suppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.ui.core.Density
+import androidx.ui.core.PxPosition
+import androidx.ui.core.Sp
+import androidx.ui.core.px
+import androidx.ui.core.sp
+import androidx.ui.core.withDensity
+import androidx.ui.engine.geometry.Rect
+import androidx.ui.text.style.TextDirection
+import androidx.ui.text.FontTestData.Companion.BASIC_MEASURE_FONT
+import androidx.ui.text.font.FontFamily
+import androidx.ui.text.font.asFontFamily
+import androidx.ui.painting.Path
+import androidx.ui.painting.PathOperation
+import androidx.ui.text.style.TextAlign
+import androidx.ui.text.style.TextIndent
+import com.nhaarman.mockitokotlin2.mock
+import org.hamcrest.Matchers.equalTo
+import org.junit.Assert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+@SmallTest
+class MultiParagraphIntegrationTest {
+    private val fontFamilyMeasureFont = BASIC_MEASURE_FONT.asFontFamily()
+    private val context = InstrumentationRegistry.getInstrumentation().context
+    private val defaultDensity = Density(density = 1f)
+    private val cursorWidth = 4f
+
+    @Test
+    fun empty_string() {
+        withDensity(defaultDensity) {
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val text = ""
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = 100.0f))
+
+            assertThat(paragraph.width, equalTo(100.0f))
+
+            assertThat(paragraph.height, equalTo(fontSizeInPx))
+            // defined in sample_font
+            assertThat(paragraph.baseline, equalTo(fontSizeInPx * 0.8f))
+            assertThat(paragraph.maxIntrinsicWidth, equalTo(0.0f))
+            assertThat(paragraph.minIntrinsicWidth, equalTo(0.0f))
+            // TODO(Migration/siyamed): no baseline query per line?
+            // TODO(Migration/siyamed): no line count?
+        }
+    }
+
+    @Test
+    fun single_line_default_values() {
+        withDensity(defaultDensity) {
+            val fontSize = 50.sp
+            val fontSizeInpx = fontSize.toPx().value
+
+            for (text in arrayOf("xyz", "\u05D0\u05D1\u05D2")) {
+                val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+                // width greater than text width - 150
+                paragraph.layout(ParagraphConstraints(width = 200.0f))
+
+                assertThat(text, paragraph.width, equalTo(200.0f))
+                assertThat(text, paragraph.height, equalTo(fontSizeInpx))
+                // defined in sample_font
+                assertThat(text, paragraph.baseline, equalTo(fontSizeInpx * 0.8f))
+                assertThat(
+                    text,
+                    paragraph.maxIntrinsicWidth,
+                    equalTo(fontSizeInpx * text.length)
+                )
+                assertThat(text, paragraph.minIntrinsicWidth, equalTo(0.0f))
+            }
+        }
+    }
+
+    @Test
+    fun line_break_default_values() {
+        withDensity(defaultDensity) {
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+
+            for (text in arrayOf("abcdef", "\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5")) {
+                val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+                // 3 chars width
+                paragraph.layout(ParagraphConstraints(width = 3 * fontSizeInPx))
+
+                // 3 chars
+                assertThat(text, paragraph.width, equalTo(3 * fontSizeInPx))
+                // 2 lines, 1 line gap
+                assertThat(
+                    text,
+                    paragraph.height,
+                    equalTo(2 * fontSizeInPx + fontSizeInPx / 5.0f)
+                )
+                // defined in sample_font
+                assertThat(text, paragraph.baseline, equalTo(fontSizeInPx * 0.8f))
+                assertThat(
+                    text,
+                    paragraph.maxIntrinsicWidth,
+                    equalTo(fontSizeInPx * text.length)
+                )
+                assertThat(text, paragraph.minIntrinsicWidth, equalTo(0.0f))
+            }
+        }
+    }
+
+    @Test
+    fun didExceedMaxLines_withMaxLinesSmallerThanTextLines_returnsTrue() {
+        val text = "aaa\naa"
+        val maxLines = text.lines().size - 1
+        val paragraph = simpleMultiParagraph(text = text, maxLines = maxLines)
+
+        paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+        assertThat(paragraph.didExceedMaxLines, equalTo(true))
+    }
+
+    @Test
+    fun didExceedMaxLines_withMaxLinesEqualToTextLines_returnsFalse() {
+        val text = "aaa\naa"
+        val maxLines = text.lines().size
+        val paragraph = simpleMultiParagraph(text = text, maxLines = maxLines)
+
+        paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+        assertThat(paragraph.didExceedMaxLines, equalTo(false))
+    }
+
+    @Test
+    fun didExceedMaxLines_withMaxLinesGreaterThanTextLines_returnsFalse() {
+        val text = "aaa\naa"
+        val maxLines = text.lines().size + 1
+        val paragraph = simpleMultiParagraph(text = text, maxLines = maxLines)
+
+        paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+        assertThat(paragraph.didExceedMaxLines, equalTo(false))
+    }
+
+    @Test
+    fun didExceedMaxLines_withMaxLinesSmallerThanTextLines_withLineWrap_returnsTrue() {
+        withDensity(defaultDensity) {
+            val text = "aa"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val maxLines = 1
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize,
+                maxLines = maxLines
+            )
+
+            // One line can only contain 1 character
+            paragraph.layout(ParagraphConstraints(width = fontSizeInPx))
+            assertThat(paragraph.didExceedMaxLines, equalTo(true))
+        }
+    }
+
+    @Test
+    fun didExceedMaxLines_withMaxLinesEqualToTextLines_withLineWrap_returnsFalse() {
+        val text = "a"
+        val maxLines = text.lines().size
+        val paragraph = simpleMultiParagraph(text = text, fontSize = 50.sp, maxLines = maxLines)
+
+        paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+        assertThat(paragraph.didExceedMaxLines, equalTo(false))
+    }
+
+    @Test
+    fun didExceedMaxLines_withMaxLinesGreaterThanTextLines_withLineWrap_returnsFalse() {
+        withDensity(defaultDensity) {
+            val text = "aa"
+            val maxLines = 3
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize,
+                maxLines = maxLines
+            )
+
+            // One line can only contain 1 character
+            paragraph.layout(ParagraphConstraints(width = fontSizeInPx))
+            assertThat(paragraph.didExceedMaxLines, equalTo(false))
+        }
+    }
+
+    @Test
+    fun getOffsetForPosition_ltr() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+            // test positions that are 1, fontSize+1, 2fontSize+1 which maps to chars 0, 1, 2 ...
+            for (i in 0..text.length) {
+                val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx / 2).px)
+                val offset = paragraph.getOffsetForPosition(position)
+                assertThat(
+                    "offset at index $i, position $position does not match",
+                    offset,
+                    equalTo(i)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getOffsetForPosition_rtl() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            // test positions that are 1, fontSize+1, 2fontSize+1 which maps to chars .., 2, 1, 0
+            for (i in 0..text.length) {
+                val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx / 2).px)
+                val offset = paragraph.getOffsetForPosition(position)
+                assertThat(
+                    "offset at index $i, position $position does not match",
+                    offset,
+                    equalTo(text.length - i)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getOffsetForPosition_ltr_multiline() {
+        withDensity(defaultDensity) {
+            val firstLine = "abc"
+            val secondLine = "def"
+            val text = firstLine + secondLine
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = firstLine.length * fontSizeInPx))
+
+            // test positions are 1, fontSize+1, 2fontSize+1 and always on the second line
+            // which maps to chars 3, 4, 5
+            for (i in 0..secondLine.length) {
+                val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx * 1.5f).px)
+                val offset = paragraph.getOffsetForPosition(position)
+                assertThat(
+                    "offset at index $i, position $position, second line does not match",
+                    offset,
+                    equalTo(i + firstLine.length)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getOffsetForPosition_rtl_multiline() {
+        withDensity(defaultDensity) {
+            val firstLine = "\u05D0\u05D1\u05D2"
+            val secondLine = "\u05D3\u05D4\u05D5"
+            val text = firstLine + secondLine
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = firstLine.length * fontSizeInPx))
+
+            // test positions are 1, fontSize+1, 2fontSize+1 and always on the second line
+            // which maps to chars 5, 4, 3
+            for (i in 0..secondLine.length) {
+                val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx * 1.5f).px)
+                val offset = paragraph.getOffsetForPosition(position)
+                assertThat(
+                    "offset at index $i, position $position, second line does not match",
+                    offset,
+                    equalTo(text.length - i)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getOffsetForPosition_ltr_width_outOfBounds() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            // greater than width
+            var position = PxPosition((fontSizeInPx * text.length * 2).px, (fontSizeInPx / 2).px)
+            var offset = paragraph.getOffsetForPosition(position)
+            assertThat(offset, equalTo(text.length))
+
+            // negative
+            position = PxPosition((-1 * fontSizeInPx).px, (fontSizeInPx / 2).px)
+            offset = paragraph.getOffsetForPosition(position)
+            assertThat(offset, equalTo(0))
+        }
+    }
+
+    @Test
+    fun getOffsetForPosition_ltr_height_outOfBounds() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            // greater than height
+            var position = PxPosition((fontSizeInPx / 2).px, (fontSizeInPx * text.length * 2).px)
+            var offset = paragraph.getOffsetForPosition(position)
+            assertThat(offset, equalTo(0))
+
+            // negative
+            position = PxPosition((fontSizeInPx / 2).px, (-1 * fontSizeInPx).px)
+            offset = paragraph.getOffsetForPosition(position)
+            assertThat(offset, equalTo(0))
+        }
+    }
+
+    @Test
+    fun getOffsetForPosition_lineBreak() {
+        withDensity(defaultDensity) {
+            val text = "abc\n"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            assertThat(
+                paragraph.getOffsetForPosition(PxPosition((3 * fontSizeInPx).px, 0.px)),
+                equalTo(3)
+            )
+
+            assertThat(
+                paragraph.getOffsetForPosition(PxPosition(0.px, (fontSizeInPx * 1.5f).px)),
+                equalTo(4)
+            )
+        }
+    }
+
+    @Test
+    fun getOffsetForPosition_multiple_paragraph() {
+        withDensity(defaultDensity) {
+            val text = "abcdef"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize,
+                paragraphStyles = listOf(
+                    AnnotatedString.Item(ParagraphStyle(), 0, 3)
+                )
+            )
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            for (i in 0 until 3) {
+                assertThat(
+                    paragraph.getOffsetForPosition(PxPosition((i * fontSizeInPx).px, 0.px)),
+                    equalTo(i)
+                )
+            }
+
+            for (i in 3 until 6) {
+                assertThat(
+                    paragraph.getOffsetForPosition(
+                        PxPosition(((i - 3) * fontSizeInPx).px, fontSizeInPx.px)
+                    ),
+                    equalTo(i)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getBoundingBox_ltr_singleLine() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+            for (i in 0 until text.length) {
+                val box = paragraph.getBoundingBox(i)
+                assertThat(box.left, equalTo(i * fontSizeInPx))
+                assertThat(box.right, equalTo((i + 1) * fontSizeInPx))
+                assertThat(box.top, equalTo(0f))
+                assertThat(box.bottom, equalTo(fontSizeInPx))
+            }
+        }
+    }
+
+    @Test
+    fun getBoundingBox_ltr_multiLines() {
+        withDensity(defaultDensity) {
+            val firstLine = "abc"
+            val secondLine = "def"
+            val text = firstLine + secondLine
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = firstLine.length * fontSizeInPx))
+
+            // test positions are 3, 4, 5 and always on the second line
+            // which maps to chars 3, 4, 5
+            for (i in 0..secondLine.length - 1) {
+                val textPosition = i + firstLine.length
+                val box = paragraph.getBoundingBox(textPosition)
+                assertThat(box.left, equalTo(i * fontSizeInPx))
+                assertThat(box.right, equalTo((i + 1) * fontSizeInPx))
+                assertThat(box.top, equalTo(fontSizeInPx))
+                assertThat(box.bottom, equalTo((2f + 1 / 5f) * fontSizeInPx))
+            }
+        }
+    }
+
+    @Test(expected = java.lang.IndexOutOfBoundsException::class)
+    fun getBoundingBox_ltr_textPosition_negative() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            paragraph.getBoundingBox(-1)
+        }
+    }
+
+    @Suppress
+    @Test(expected = java.lang.IndexOutOfBoundsException::class)
+    fun getBoundingBox_ltr_textPosition_larger_than_length_throw_exception() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            val textPosition = text.length + 1
+            paragraph.getBoundingBox(textPosition)
+        }
+    }
+
+    @Test(expected = java.lang.AssertionError::class)
+    fun getCursorRect_larger_than_length_throw_exception() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            paragraph.getCursorRect(text.length + 1)
+        }
+    }
+
+    @Test(expected = java.lang.AssertionError::class)
+    fun getCursorRect_negative_throw_exception() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            paragraph.getCursorRect(-1)
+        }
+    }
+
+    @Test
+    fun getCursorRect_ltr_singleLine() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            for (i in 0 until text.length) {
+                val cursorRect = paragraph.getCursorRect(i)
+                val cursorXOffset = i * fontSizeInPx
+                assertThat(
+                    cursorRect,
+                    equalTo(Rect(
+                        left = cursorXOffset - cursorWidth / 2,
+                        top = 0f,
+                        right = cursorXOffset + cursorWidth / 2,
+                        bottom = fontSizeInPx
+                    ))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getCursorRect_ltr_multiLines() {
+        withDensity(defaultDensity) {
+            val text = "abcdef"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+            val charsPerLine = 3
+
+            paragraph.layout(ParagraphConstraints(width = charsPerLine * fontSizeInPx))
+
+            for (i in 0 until charsPerLine) {
+                val cursorXOffset = i * fontSizeInPx
+                assertThat(
+                    paragraph.getCursorRect(i),
+                    equalTo(Rect(
+                        left = cursorXOffset - cursorWidth / 2,
+                        top = 0f,
+                        right = cursorXOffset + cursorWidth / 2,
+                        bottom = fontSizeInPx
+                    ))
+                )
+            }
+
+            for (i in charsPerLine until text.length) {
+                val cursorXOffset = (i % charsPerLine) * fontSizeInPx
+                assertThat(
+                    paragraph.getCursorRect(i),
+                    equalTo(Rect(
+                        left = cursorXOffset - cursorWidth / 2,
+                        top = fontSizeInPx,
+                        right = cursorXOffset + cursorWidth / 2,
+                        bottom = fontSizeInPx * 2.2f
+                    ))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getCursorRect_rtl_singleLine() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            for (i in 0 until text.length) {
+                val cursorXOffset = (text.length - i) * fontSizeInPx
+                assertThat(
+                    paragraph.getCursorRect(i),
+                    equalTo(Rect(
+                        left = cursorXOffset - cursorWidth / 2,
+                        top = 0f,
+                        right = cursorXOffset + cursorWidth / 2,
+                        bottom = fontSizeInPx
+                    ))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getCursorRect_rtl_multiLines() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2\u05D0\u05D1\u05D2"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+            val charsPerLine = 3
+
+            paragraph.layout(ParagraphConstraints(width = charsPerLine * fontSizeInPx))
+
+            for (i in 0 until charsPerLine) {
+                val cursorXOffset = (charsPerLine - i) * fontSizeInPx
+                assertThat(
+                    paragraph.getCursorRect(i),
+                    equalTo(Rect(
+                        left = cursorXOffset - cursorWidth / 2,
+                        top = 0f,
+                        right = cursorXOffset + cursorWidth / 2,
+                        bottom = fontSizeInPx
+                    ))
+                )
+            }
+
+            for (i in charsPerLine until text.length) {
+                val cursorXOffset = (charsPerLine - i % charsPerLine) * fontSizeInPx
+                assertThat(
+                    paragraph.getCursorRect(i),
+                    equalTo(Rect(
+                        left = cursorXOffset - cursorWidth / 2,
+                        top = fontSizeInPx,
+                        right = cursorXOffset + cursorWidth / 2,
+                        bottom = fontSizeInPx * 2.2f
+                    ))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_ltr_singleLine_textDirectionDefault() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            for (i in 0..text.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(fontSizeInPx * i)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_rtl_singleLine_textDirectionDefault() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            for (i in 0..text.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(width - fontSizeInPx * i)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_bidi_singleLine_textDirectionDefault() {
+        withDensity(defaultDensity) {
+            val ltrText = "abc"
+            val rtlText = "\u05D0\u05D1\u05D2"
+            val text = ltrText + rtlText
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            for (i in 0..ltrText.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(fontSizeInPx * i)
+                )
+            }
+
+            for (i in 1 until rtlText.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i + ltrText.length),
+                    equalTo(width - fontSizeInPx * i)
+                )
+            }
+
+            assertThat(
+                paragraph.getPrimaryHorizontal(text.length),
+                equalTo(width)
+            )
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_ltr_singleLine_textDirectionRtl() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize,
+                textDirection = TextDirection.Rtl
+            )
+            val width = text.length * fontSizeInPx
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(0), equalTo(width))
+
+            for (i in 1 until text.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(fontSizeInPx * i)
+                )
+            }
+
+            assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_rtl_singleLine_textDirectionLtr() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize,
+                textDirection = TextDirection.Ltr
+            )
+            val width = text.length * fontSizeInPx
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(0), equalTo(0f))
+
+            for (i in 1 until text.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(width - fontSizeInPx * i)
+                )
+            }
+
+            assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(width))
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_bidi_singleLine_textDirectionLtr() {
+        withDensity(defaultDensity) {
+            val ltrText = "abc"
+            val rtlText = "\u05D0\u05D1\u05D2"
+            val text = ltrText + rtlText
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize,
+                textDirection = TextDirection.Ltr
+            )
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            for (i in 0..ltrText.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(fontSizeInPx * i)
+                )
+            }
+
+            for (i in 1 until rtlText.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i + ltrText.length),
+                    equalTo(width - fontSizeInPx * i)
+                )
+            }
+
+            assertThat(
+                paragraph.getPrimaryHorizontal(text.length),
+                equalTo(width)
+            )
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_bidi_singleLine_textDirectionRtl() {
+        withDensity(defaultDensity) {
+            val ltrText = "abc"
+            val rtlText = "\u05D0\u05D1\u05D2"
+            val text = ltrText + rtlText
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize,
+                textDirection = TextDirection.Rtl
+            )
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(0), equalTo(width))
+            // Notice that abc is
+            for (i in 1 until ltrText.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(rtlText.length * fontSizeInPx + i * fontSizeInPx)
+                )
+            }
+
+            for (i in 0..rtlText.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i + ltrText.length),
+                    equalTo(rtlText.length * fontSizeInPx - i * fontSizeInPx)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_ltr_newLine_textDirectionDefault() {
+        withDensity(defaultDensity) {
+            val text = "abc\n"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_rtl_newLine_textDirectionDefault() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2\n"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_ltr_newLine_textDirectionRtl() {
+        withDensity(defaultDensity) {
+            val text = "abc\n"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize,
+                textDirection = TextDirection.Rtl
+            )
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(width))
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_rtl_newLine_textDirectionLtr() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2\n"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize,
+                textDirection = TextDirection.Ltr
+            )
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+        }
+    }
+
+    @Test
+    fun getLineForOffset_singleLine() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            for (i in 0..text.lastIndex) {
+                assertThat(paragraph.getLineForOffset(i), equalTo(0))
+            }
+        }
+    }
+
+    @Test
+    fun getLineForOffset_multiLines() {
+        withDensity(defaultDensity) {
+            val text = "a\nb\nc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            for (i in 0..text.lastIndex) {
+                assertThat(paragraph.getLineForOffset(i), equalTo(i / 2))
+            }
+        }
+    }
+
+    @Test
+    fun getLineForOffset_multiParagraph() {
+        withDensity(defaultDensity) {
+            val text = "abcd"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize,
+                paragraphStyles = listOf(AnnotatedString.Item(ParagraphStyle(), 0, 2))
+            )
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+            assertThat(paragraph.getLineForOffset(0), equalTo(0))
+            assertThat(paragraph.getLineForOffset(1), equalTo(0))
+            assertThat(paragraph.getLineForOffset(2), equalTo(1))
+            assertThat(paragraph.getLineForOffset(3), equalTo(1))
+        }
+    }
+
+    @Test
+    fun getLineForOffset_emptyParagraph() {
+        withDensity(defaultDensity) {
+            val text = "abcd"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize,
+                paragraphStyles = listOf(AnnotatedString.Item(ParagraphStyle(), 2, 2))
+            )
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+            assertThat(paragraph.getLineForOffset(0), equalTo(0))
+            assertThat(paragraph.getLineForOffset(1), equalTo(0))
+            // The empty paragraph takes one line
+            assertThat(paragraph.getLineForOffset(2), equalTo(2))
+            assertThat(paragraph.getLineForOffset(3), equalTo(2))
+        }
+    }
+
+    @Test(expected = java.lang.IndexOutOfBoundsException::class)
+    fun getLineForOffset_negativeOffset() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            paragraph.getLineForOffset(-1)
+        }
+    }
+
+    @Test(expected = java.lang.IndexOutOfBoundsException::class)
+    fun getLineForOffset_outOfBoundary() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(text = text, fontSize = fontSize)
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            paragraph.getLineForOffset(text.length)
+        }
+    }
+
+    @Test
+    fun testGetPathForRange_singleLine() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontFamily = fontFamilyMeasureFont,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+            val expectedPath = Path()
+            val lineLeft = paragraph.getLineLeft(0)
+            val lineRight = paragraph.getLineRight(0)
+            expectedPath.addRect(
+                Rect(
+                    lineLeft,
+                    0f,
+                    lineRight - fontSizeInPx,
+                    fontSizeInPx
+                )
+            )
+
+            // Select "ab"
+            val actualPath = paragraph.getPathForRange(0, 2)
+
+            val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+            assertThat(diff, equalTo(Rect.zero))
+        }
+    }
+
+    @Test
+    fun testGetPathForRange_multiLines() {
+        withDensity(defaultDensity) {
+            val text = "abc\nabc"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontFamily = fontFamilyMeasureFont,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+            val expectedPath = Path()
+            val firstLineLeft = paragraph.getLineLeft(0)
+            val secondLineLeft = paragraph.getLineLeft(1)
+            val firstLineRight = paragraph.getLineRight(0)
+            val secondLineRight = paragraph.getLineRight(1)
+            expectedPath.addRect(
+                Rect(
+                    firstLineLeft + fontSizeInPx,
+                    0f,
+                    firstLineRight,
+                    fontSizeInPx
+                )
+            )
+            expectedPath.addRect(
+                Rect(
+                    secondLineLeft,
+                    fontSizeInPx,
+                    secondLineRight - fontSizeInPx,
+                    paragraph.height
+                )
+            )
+
+            // Select "bc\nab"
+            val actualPath = paragraph.getPathForRange(1, 6)
+
+            val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+            assertThat(diff, equalTo(Rect.zero))
+        }
+    }
+
+    @Test
+    fun testGetPathForRange_Bidi() {
+        withDensity(defaultDensity) {
+            val textLTR = "Hello"
+            val textRTL = "שלום"
+            val text = textLTR + textRTL
+            val selectionLTRStart = 2
+            val selectionRTLEnd = 2
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontFamily = fontFamilyMeasureFont,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+            val expectedPath = Path()
+            val lineLeft = paragraph.getLineLeft(0)
+            val lineRight = paragraph.getLineRight(0)
+            expectedPath.addRect(
+                Rect(
+                    lineLeft + selectionLTRStart * fontSizeInPx,
+                    0f,
+                    lineLeft + textLTR.length * fontSizeInPx,
+                    fontSizeInPx
+                )
+            )
+            expectedPath.addRect(
+                Rect(
+                    lineRight - selectionRTLEnd * fontSizeInPx,
+                    0f,
+                    lineRight,
+                    fontSizeInPx
+                )
+            )
+
+            // Select "llo..של"
+            val actualPath =
+                paragraph.getPathForRange(selectionLTRStart, textLTR.length + selectionRTLEnd)
+
+            val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+            assertThat(diff, equalTo(Rect.zero))
+        }
+    }
+
+    @Test
+    fun testGetPathForRange_Start_Equals_End_Returns_Empty_Path() {
+        val text = "abc"
+        val paragraph = simpleMultiParagraph(
+            text = text,
+            fontFamily = fontFamilyMeasureFont,
+            fontSize = 20.sp
+        )
+        paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+        val actualPath = paragraph.getPathForRange(1, 1)
+
+        assertThat(actualPath.getBounds(), equalTo(Rect.zero))
+    }
+
+    @Test
+    fun testGetPathForRange_Empty_Text() {
+        val text = ""
+        val paragraph = simpleMultiParagraph(
+            text = text,
+            fontFamily = fontFamilyMeasureFont,
+            fontSize = 20.sp
+        )
+        paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+        val actualPath = paragraph.getPathForRange(0, 0)
+
+        assertThat(actualPath.getBounds(), equalTo(Rect.zero))
+    }
+
+    @Test
+    fun testGetPathForRange_Surrogate_Pair_Start_Middle_Second_Character_Selected() {
+        withDensity(defaultDensity) {
+            val text = "\uD834\uDD1E\uD834\uDD1F"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontFamily = fontFamilyMeasureFont,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+            val expectedPath = Path()
+            val lineRight = paragraph.getLineRight(0)
+            expectedPath.addRect(Rect(lineRight / 2, 0f, lineRight, fontSizeInPx))
+
+            // Try to select "\uDD1E\uD834\uDD1F", only "\uD834\uDD1F" is selected.
+            val actualPath = paragraph.getPathForRange(1, text.length)
+
+            val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+            assertThat(diff, equalTo(Rect.zero))
+        }
+    }
+
+    @Test
+    fun testGetPathForRange_Surrogate_Pair_End_Middle_Second_Character_Selected() {
+        withDensity(defaultDensity) {
+            val text = "\uD834\uDD1E\uD834\uDD1F"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontFamily = fontFamilyMeasureFont,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+            val expectedPath = Path()
+            val lineRight = paragraph.getLineRight(0)
+            expectedPath.addRect(Rect(lineRight / 2, 0f, lineRight, fontSizeInPx))
+
+            // Try to select "\uDD1E\uD834", actually "\uD834\uDD1F" is selected.
+            val actualPath = paragraph.getPathForRange(1, text.length - 1)
+
+            val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+            assertThat(diff, equalTo(Rect.zero))
+        }
+    }
+
+    @Test
+    fun testGetPathForRange_Surrogate_Pair_Start_Middle_End_Same_Character_Returns_Line_Segment() {
+        withDensity(defaultDensity) {
+            val text = "\uD834\uDD1E\uD834\uDD1F"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontFamily = fontFamilyMeasureFont,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+            val expectedPath = Path()
+            val lineRight = paragraph.getLineRight(0)
+            expectedPath.addRect(Rect(lineRight / 2, 0f, lineRight / 2, fontSizeInPx))
+
+            // Try to select "\uDD1E", get vertical line segment after this character.
+            val actualPath = paragraph.getPathForRange(1, 2)
+
+            val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+            assertThat(diff, equalTo(Rect.zero))
+        }
+    }
+
+    @Test
+    fun testGetPathForRange_Emoji_Sequence() {
+        withDensity(defaultDensity) {
+            val text = "\u1F600\u1F603\u1F604\u1F606"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontFamily = fontFamilyMeasureFont,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+            val expectedPath = Path()
+            val lineLeft = paragraph.getLineLeft(0)
+            val lineRight = paragraph.getLineRight(0)
+            expectedPath.addRect(
+                Rect(
+                    lineLeft + fontSizeInPx,
+                    0f,
+                    lineRight - fontSizeInPx,
+                    fontSizeInPx
+                )
+            )
+
+            // Select "\u1F603\u1F604"
+            val actualPath = paragraph.getPathForRange(1, text.length - 1)
+
+            val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+            assertThat(diff, equalTo(Rect.zero))
+        }
+    }
+
+    @Test
+    fun testGetPathForRange_Unicode_200D_Return_Line_Segment() {
+        withDensity(defaultDensity) {
+            val text = "\u200D"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontFamily = fontFamilyMeasureFont,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+            val expectedPath = Path()
+            val lineLeft = paragraph.getLineLeft(0)
+            val lineRight = paragraph.getLineRight(0)
+            expectedPath.addRect(Rect(lineLeft, 0f, lineRight, fontSizeInPx))
+
+            val actualPath = paragraph.getPathForRange(0, 1)
+
+            assertThat(lineLeft, equalTo(lineRight))
+            val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+            assertThat(diff, equalTo(Rect.zero))
+        }
+    }
+
+    @Test
+    fun testGetPathForRange_Unicode_2066_Return_Line_Segment() {
+        withDensity(defaultDensity) {
+            val text = "\u2066"
+            val fontSize = 20f.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontFamily = fontFamilyMeasureFont,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+            val expectedPath = Path()
+            val lineLeft = paragraph.getLineLeft(0)
+            val lineRight = paragraph.getLineRight(0)
+            expectedPath.addRect(Rect(lineLeft, 0f, lineRight, fontSizeInPx))
+
+            val actualPath = paragraph.getPathForRange(0, 1)
+
+            assertThat(lineLeft, equalTo(lineRight))
+            val diff = Path.combine(PathOperation.difference, expectedPath, actualPath).getBounds()
+            assertThat(diff, equalTo(Rect.zero))
+        }
+    }
+
+    @Test
+    fun testGetWordBoundary() {
+        val text = "abc def"
+        val paragraph = simpleMultiParagraph(
+            text = text,
+            fontFamily = fontFamilyMeasureFont,
+            fontSize = 20.sp
+        )
+        paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+        val result = paragraph.getWordBoundary(text.indexOf('a'))
+
+        assertThat(result.start, equalTo(text.indexOf('a')))
+        assertThat(result.end, equalTo(text.indexOf(' ')))
+    }
+
+    @Test
+    fun testGetWordBoundary_Bidi() {
+        val text = "abc \u05d0\u05d1\u05d2 def"
+        val paragraph = simpleMultiParagraph(
+            text = text,
+            fontFamily = fontFamilyMeasureFont,
+            fontSize = 20.sp
+        )
+        paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+        val resultEnglish = paragraph.getWordBoundary(text.indexOf('a'))
+        val resultHebrew = paragraph.getWordBoundary(text.indexOf('\u05d1'))
+
+        assertThat(resultEnglish.start, equalTo(text.indexOf('a')))
+        assertThat(resultEnglish.end, equalTo(text.indexOf(' ')))
+        assertThat(resultHebrew.start, equalTo(text.indexOf('\u05d0')))
+        assertThat(resultHebrew.end, equalTo(text.indexOf('\u05d2') + 1))
+    }
+
+    @Test
+    fun width_default_value() {
+        val paragraph = simpleMultiParagraph()
+
+        assertThat(paragraph.width, equalTo(0.0f))
+    }
+
+    @Test
+    fun height_default_value() {
+        val paragraph = simpleMultiParagraph()
+
+        assertThat(paragraph.height, equalTo(0.0f))
+    }
+
+    @Test
+    fun minIntrinsicWidth_default_value() {
+        val paragraph = simpleMultiParagraph()
+
+        assertThat(paragraph.minIntrinsicWidth, equalTo(0.0f))
+    }
+
+    @Test
+    fun maxIntrinsicWidth_default_value() {
+        val paragraph = simpleMultiParagraph()
+
+        assertThat(paragraph.maxIntrinsicWidth, equalTo(0.0f))
+    }
+
+    @Test
+    fun alphabeticBaseline_default_value() {
+        val paragraph = simpleMultiParagraph()
+
+        assertThat(paragraph.baseline, equalTo(0.0f))
+    }
+
+    @Test
+    fun didExceedMaxLines_default_value() {
+        val paragraph = simpleMultiParagraph()
+
+        assertThat(paragraph.didExceedMaxLines, equalTo(false))
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun paint_throws_exception_if_layout_is_not_called() {
+        val paragraph = simpleMultiParagraph()
+
+        paragraph.paint(mock())
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun getPositionForOffset_throws_exception_if_layout_is_not_called() {
+        val paragraph = simpleMultiParagraph()
+
+        paragraph.getOffsetForPosition(PxPosition.Origin)
+    }
+
+    @Test(expected = AssertionError::class)
+    fun getPathForRange_throws_exception_if_start_larger_than_end() {
+        val text = "ab"
+        val textStart = 0
+        val textEnd = text.length
+        val paragraph = simpleMultiParagraph(text = text)
+
+        paragraph.getPathForRange(textEnd, textStart)
+    }
+
+    @Test(expected = AssertionError::class)
+    fun getPathForRange_throws_exception_if_start_is_smaller_than_zero() {
+        val text = "ab"
+        val textStart = 0
+        val textEnd = text.length
+        val paragraph = simpleMultiParagraph(text = text)
+
+        paragraph.getPathForRange(textStart - 2, textEnd - 1)
+    }
+
+    @Test(expected = AssertionError::class)
+    fun getPathForRange_throws_exception_if_end_is_larger_than_text_length() {
+        val text = "ab"
+        val textStart = 0
+        val textEnd = text.length
+        val paragraph = simpleMultiParagraph(text = text)
+
+        paragraph.getPathForRange(textStart, textEnd + 1)
+    }
+
+    @Test
+    fun textAlign_defaultValue_alignsStart() {
+        withDensity(defaultDensity) {
+            val textLTR = "aa"
+            val textRTL = "\u05D0\u05D0"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+
+            val paragraphLTR = simpleMultiParagraph(
+                text = textLTR,
+                fontSize = fontSize
+            )
+            val layoutLTRWidth = (textLTR.length + 2) * fontSizeInPx
+            paragraphLTR.layout(ParagraphConstraints(width = layoutLTRWidth))
+
+            val paragraphRTL = simpleMultiParagraph(
+                text = textRTL,
+                fontSize = fontSize
+            )
+            val layoutRTLWidth = (textRTL.length + 2) * fontSizeInPx
+            paragraphRTL.layout(ParagraphConstraints(width = layoutRTLWidth))
+
+            // When textAlign is TextAlign.start, LTR aligns to left, RTL aligns to right.
+            assertThat(paragraphLTR.getLineLeft(0), equalTo(0.0f))
+            assertThat(paragraphRTL.getLineRight(0), equalTo(layoutRTLWidth))
+        }
+    }
+
+    @Test
+    fun textAlign_whenAlignLeft_returnsZeroForGetLineLeft() {
+        withDensity(defaultDensity) {
+            val texts = listOf("aa", "\u05D0\u05D0")
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+
+            texts.map { text ->
+                val paragraph = simpleMultiParagraph(
+                    text = text,
+                    textAlign = TextAlign.Left,
+                    fontSize = fontSize
+                )
+                val layoutWidth = (text.length + 2) * fontSizeInPx
+                paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+                assertThat(paragraph.getLineLeft(0), equalTo(0.0f))
+            }
+        }
+    }
+
+    @Test
+    fun textAlign_whenAlignRight_returnsLayoutWidthForGetLineRight() {
+        withDensity(defaultDensity) {
+            val texts = listOf("aa", "\u05D0\u05D0")
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+
+            texts.map { text ->
+                val paragraph = simpleMultiParagraph(
+                    text = text,
+                    textAlign = TextAlign.Right,
+                    fontSize = fontSize
+                )
+                val layoutWidth = (text.length + 2) * fontSizeInPx
+                paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+                assertThat(paragraph.getLineRight(0), equalTo(layoutWidth))
+            }
+        }
+    }
+
+    @Test
+    fun textAlign_whenAlignCenter_textIsCentered() {
+        withDensity(defaultDensity) {
+            val texts = listOf("aa", "\u05D0\u05D0")
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+
+            texts.map { text ->
+                val paragraph = simpleMultiParagraph(
+                    text = text,
+                    textAlign = TextAlign.Center,
+                    fontSize = fontSize
+                )
+                val layoutWidth = (text.length + 2) * fontSizeInPx
+                paragraph.layout(ParagraphConstraints(width = layoutWidth))
+                val textWidth = text.length * fontSizeInPx
+
+                assertThat(
+                    paragraph.getLineLeft(0),
+                    equalTo(layoutWidth / 2 - textWidth / 2)
+                )
+                assertThat(
+                    paragraph.getLineRight(0),
+                    equalTo(layoutWidth / 2 + textWidth / 2)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun textAlign_whenAlignStart_withLTR_returnsZeroForGetLineLeft() {
+        withDensity(defaultDensity) {
+            val text = "aa"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val layoutWidth = (text.length + 2) * fontSizeInPx
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                textAlign = TextAlign.Start,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+            assertThat(paragraph.getLineLeft(0), equalTo(0.0f))
+        }
+    }
+
+    @Test
+    fun textAlign_whenAlignEnd_withLTR_returnsLayoutWidthForGetLineRight() {
+        withDensity(defaultDensity) {
+            val text = "aa"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val layoutWidth = (text.length + 2) * fontSizeInPx
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                textAlign = TextAlign.End,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+            assertThat(paragraph.getLineRight(0), equalTo(layoutWidth))
+        }
+    }
+
+    @Test
+    fun textAlign_whenAlignStart_withRTL_returnsLayoutWidthForGetLineRight() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D0"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val layoutWidth = (text.length + 2) * fontSizeInPx
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                textAlign = TextAlign.Start,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+            assertThat(paragraph.getLineRight(0), equalTo(layoutWidth))
+        }
+    }
+
+    @Test
+    fun textAlign_whenAlignEnd_withRTL_returnsZeroForGetLineLeft() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D0"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val layoutWidth = (text.length + 2) * fontSizeInPx
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                textAlign = TextAlign.End,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+            assertThat(paragraph.getLineLeft(0), equalTo(0.0f))
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 28)
+    // We have to test justification above API 28 because of this bug b/68009059, where devices
+    // before API 28 may have an extra space at the end of line.
+    fun textAlign_whenAlignJustify_justifies() {
+        withDensity(defaultDensity) {
+            val text = "a a a"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val layoutWidth = ("a a".length + 1) * fontSizeInPx
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                textAlign = TextAlign.Justify,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+            assertThat(paragraph.getLineLeft(0), equalTo(0.0f))
+            assertThat(paragraph.getLineRight(0), equalTo(layoutWidth))
+            // Last line should align start
+            assertThat(paragraph.getLineLeft(1), equalTo(0.0f))
+        }
+    }
+
+    @Test
+    fun textDirection_whenLTR_dotIsOnRight() {
+        withDensity(defaultDensity) {
+            val text = "a.."
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val layoutWidth = text.length * fontSizeInPx
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                textDirection = TextDirection.Ltr,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = layoutWidth))
+            // The position of the last character in display order.
+            val position = PxPosition(("a.".length * fontSizeInPx + 1).px, (fontSizeInPx / 2).px)
+            val charIndex = paragraph.getOffsetForPosition(position)
+            assertThat(charIndex, equalTo(2))
+        }
+    }
+
+    @Test
+    fun textDirection_whenRTL_dotIsOnLeft() {
+        withDensity(defaultDensity) {
+            val text = "a.."
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val layoutWidth = text.length * fontSizeInPx
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                textDirection = TextDirection.Rtl,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = layoutWidth))
+            // The position of the first character in display order.
+            val position = PxPosition((fontSizeInPx / 2 + 1).px, (fontSizeInPx / 2).px)
+            val charIndex = paragraph.getOffsetForPosition(position)
+            assertThat(charIndex, equalTo(2))
+        }
+    }
+
+    @Test
+    fun textDirection_whenDefault_withoutStrongChar_directionIsLTR() {
+        withDensity(defaultDensity) {
+            val text = "..."
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val layoutWidth = text.length * fontSizeInPx
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = layoutWidth))
+            for (i in 0..text.length) {
+                // The position of the i-th character in display order.
+                val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx / 2).px)
+                val charIndex = paragraph.getOffsetForPosition(position)
+                assertThat(charIndex, equalTo(i))
+            }
+        }
+    }
+
+    @Test
+    fun textDirection_whenDefault_withFirstStrongCharLTR_directionIsLTR() {
+        withDensity(defaultDensity) {
+            val text = "a\u05D0."
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val layoutWidth = text.length * fontSizeInPx
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = layoutWidth))
+            for (i in 0 until text.length) {
+                // The position of the i-th character in display order.
+                val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx / 2).px)
+                val charIndex = paragraph.getOffsetForPosition(position)
+                assertThat(charIndex, equalTo(i))
+            }
+        }
+    }
+
+    @Test
+    fun textDirection_whenDefault_withFirstStrongCharRTL_directionIsRTL() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0a."
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val layoutWidth = text.length * fontSizeInPx
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize
+            )
+            paragraph.layout(ParagraphConstraints(width = layoutWidth))
+            // The first character in display order should be '.'
+            val position = PxPosition((fontSizeInPx / 2 + 1).px, (fontSizeInPx / 2).px)
+            val index = paragraph.getOffsetForPosition(position)
+            assertThat(index, equalTo(2))
+        }
+    }
+
+    @Test
+    fun lineHeight_returnsSameAsGiven() {
+        withDensity(defaultDensity) {
+            val text = "abcdefgh"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            // Make the layout 4 lines
+            val layoutWidth = text.length * fontSizeInPx / 4
+            val lineHeight = 1.5f
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize,
+                lineHeight = lineHeight
+            )
+            paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+            assertThat(paragraph.lineCount, equalTo(4))
+            // TODO(Migration/haoyuchang): Due to bug b/120530738, the height of the first line is
+            // wrong in the framework. Will fix it when the lineHeight in TextSpan is implemented.
+            for (i in 1 until paragraph.lineCount - 1) {
+                val actualHeight = paragraph.getLineHeight(i)
+                // In the sample_font.ttf, the height of the line should be
+                // fontSize + 0.2f * fontSize(line gap)
+                assertThat(
+                    "line number $i",
+                    actualHeight,
+                    equalTo(1.2f * fontSizeInPx * lineHeight)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun lineHeight_hasNoEffectOnLastLine() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val layoutWidth = (text.length - 1) * fontSizeInPx
+            val lineHeight = 1.5f
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                fontSize = fontSize,
+                lineHeight = lineHeight
+            )
+            paragraph.layout(ParagraphConstraints(width = layoutWidth))
+
+            val lastLine = paragraph.lineCount - 1
+            // In the sample_font.ttf, the height of the line should be
+            // fontSize + 0.2 * fontSize(line gap)
+            assertThat(paragraph.getLineHeight(lastLine), equalTo(1.2f * fontSizeInPx))
+        }
+    }
+
+    @Test
+    fun textIndent_onSingleLine() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val indent = 20.0f
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                textIndent = TextIndent(firstLine = indent.px),
+                fontSize = fontSize,
+                fontFamily = fontFamilyMeasureFont
+            )
+            paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+            // This position should point to the first character 'a' if indent is applied.
+            // Otherwise this position will point to the second character 'b'.
+            val position = PxPosition((indent + 1).px, (fontSizeInPx / 2).px)
+            // The offset corresponding to the position should be the first char 'a'.
+            assertThat(paragraph.getOffsetForPosition(position), equalTo(0))
+        }
+    }
+
+    @Test
+    fun textIndent_onFirstLine() {
+        withDensity(defaultDensity) {
+            val text = "abcdef"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val indent = 20.0f
+            val paragraphWidth = "abcd".length * fontSizeInPx
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                textIndent = TextIndent(firstLine = indent.px),
+                fontSize = fontSize,
+                fontFamily = fontFamilyMeasureFont
+            )
+            paragraph.layout(ParagraphConstraints(width = paragraphWidth))
+
+            assertThat(paragraph.lineCount, equalTo(2))
+            // This position should point to the first character of the first line if indent is
+            // applied. Otherwise this position will point to the second character of the second line.
+            val position = PxPosition((indent + 1).px, (fontSizeInPx / 2).px)
+            // The offset corresponding to the position should be the first char 'a'.
+            assertThat(paragraph.getOffsetForPosition(position), equalTo(0))
+        }
+    }
+
+    @Test
+    fun textIndent_onRestLine() {
+        withDensity(defaultDensity) {
+            val text = "abcde"
+            val fontSize = 20.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val indent = 20.0f
+            val paragraphWidth = "abc".length * fontSizeInPx
+
+            val paragraph = simpleMultiParagraph(
+                text = text,
+                textIndent = TextIndent(
+                    firstLine = 0.px,
+                    restLine = indent.px
+                ),
+                fontSize = fontSize,
+                fontFamily = fontFamilyMeasureFont
+            )
+            paragraph.layout(ParagraphConstraints(width = paragraphWidth))
+
+            // This position should point to the first character of the second line if indent is
+            // applied. Otherwise this position will point to the second character of the second line.
+            val position = PxPosition((indent + 1).px, (fontSizeInPx / 2 + fontSizeInPx).px)
+            // The offset corresponding to the position should be the 'd' in the second line.
+            assertThat(
+                paragraph.getOffsetForPosition(position),
+                equalTo("abcd".length - 1)
+            )
+        }
+    }
+
+    private fun simpleMultiParagraph(
+        text: String = "",
+        textIndent: TextIndent? = null,
+        textAlign: TextAlign? = null,
+        textDirection: TextDirection? = null,
+        fontSize: Sp? = null,
+        maxLines: Int? = null,
+        lineHeight: Float? = null,
+        textStyles: List<AnnotatedString.Item<TextStyle>> = listOf(),
+        paragraphStyles: List<AnnotatedString.Item<ParagraphStyle>> = listOf(),
+        fontFamily: FontFamily = fontFamilyMeasureFont,
+        locale: Locale? = null,
+        textStyle: TextStyle? = null,
+        density: Density? = null
+    ): MultiParagraph {
+        return MultiParagraph(
+            annotatedString = AnnotatedString(
+                text = text,
+                textStyles = textStyles,
+                paragraphStyles = paragraphStyles
+            ),
+            textStyle = TextStyle(
+                fontFamily = fontFamily,
+                fontSize = fontSize,
+                locale = locale
+            ).merge(textStyle),
+            paragraphStyle = ParagraphStyle(
+                textIndent = textIndent,
+                textAlign = textAlign,
+                textDirection = textDirection,
+                lineHeight = lineHeight
+            ),
+            maxLines = maxLines,
+            density = density ?: defaultDensity,
+            resourceLoader = TestFontResourceLoader(context)
+        )
+    }
+}
diff --git a/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntegrationTest.kt b/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntegrationTest.kt
index 841eb1b..9718437 100644
--- a/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntegrationTest.kt
+++ b/ui/ui-text/src/androidTest/java/androidx/ui/text/ParagraphIntegrationTest.kt
@@ -62,6 +62,8 @@
 
     private val resourceLoader = TestFontResourceLoader(context)
 
+    private val cursorWidth = 4f
+
     @Test
     fun empty_string() {
         withDensity(defaultDensity) {
@@ -430,6 +432,566 @@
         }
     }
 
+    @Test(expected = java.lang.AssertionError::class)
+    fun getCursorRect_larger_than_length_throw_exception() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            paragraph.getCursorRect(text.length + 1)
+        }
+    }
+
+    @Test(expected = java.lang.AssertionError::class)
+    fun getCursorRect_negative_throw_exception() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            paragraph.getCursorRect(-1)
+        }
+    }
+
+    @Test
+    fun getCursorRect_ltr_singleLine() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            for (i in 0 until text.length) {
+                val cursorRect = paragraph.getCursorRect(i)
+                val cursorXOffset = i * fontSizeInPx
+                assertThat(
+                    cursorRect,
+                    equalTo(Rect(
+                        left = cursorXOffset - cursorWidth / 2,
+                        top = 0f,
+                        right = cursorXOffset + cursorWidth / 2,
+                        bottom = fontSizeInPx
+                    ))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getCursorRect_ltr_multiLines() {
+        withDensity(defaultDensity) {
+            val text = "abcdef"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+            val charsPerLine = 3
+
+            paragraph.layout(ParagraphConstraints(width = charsPerLine * fontSizeInPx))
+
+            for (i in 0 until charsPerLine) {
+                val cursorXOffset = i * fontSizeInPx
+                assertThat(
+                    paragraph.getCursorRect(i),
+                    equalTo(Rect(
+                        left = cursorXOffset - cursorWidth / 2,
+                        top = 0f,
+                        right = cursorXOffset + cursorWidth / 2,
+                        bottom = fontSizeInPx
+                    ))
+                )
+            }
+
+            for (i in charsPerLine until text.length) {
+                val cursorXOffset = (i % charsPerLine) * fontSizeInPx
+                assertThat(
+                    paragraph.getCursorRect(i),
+                    equalTo(Rect(
+                        left = cursorXOffset - cursorWidth / 2,
+                        top = fontSizeInPx,
+                        right = cursorXOffset + cursorWidth / 2,
+                        bottom = fontSizeInPx * 2.2f
+                    ))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getCursorRect_ltr_newLine() {
+        withDensity(defaultDensity) {
+            val text = "abc\ndef"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+            // Cursor before '\n'
+            assertThat(
+                paragraph.getCursorRect(3),
+                equalTo(Rect(
+                    left = 3 * fontSizeInPx - cursorWidth / 2,
+                    top = 0f,
+                    right = 3 * fontSizeInPx + cursorWidth / 2,
+                    bottom = fontSizeInPx
+                ))
+            )
+
+            // Cursor after '\n'
+            assertThat(
+                paragraph.getCursorRect(4),
+                equalTo(Rect(
+                    left = -cursorWidth / 2,
+                    top = fontSizeInPx,
+                    right = cursorWidth / 2,
+                    bottom = fontSizeInPx * 2.2f
+                ))
+            )
+        }
+    }
+
+    @Test
+    fun getCursorRect_ltr_newLine_last_char() {
+        withDensity(defaultDensity) {
+            val text = "abc\n"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
+
+            // Cursor before '\n'
+            assertThat(
+                paragraph.getCursorRect(3),
+                equalTo(Rect(
+                    left = 3 * fontSizeInPx - cursorWidth / 2,
+                    top = 0f,
+                    right = 3 * fontSizeInPx + cursorWidth / 2,
+                    bottom = fontSizeInPx
+                ))
+            )
+
+            // Cursor after '\n'
+            assertThat(
+                paragraph.getCursorRect(4),
+                equalTo(Rect(
+                    left = -cursorWidth / 2,
+                    top = fontSizeInPx,
+                    right = cursorWidth / 2,
+                    bottom = fontSizeInPx * 2.2f
+                ))
+            )
+        }
+    }
+
+    @Test
+    fun getCursorRect_rtl_singleLine() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            for (i in 0 until text.length) {
+                val cursorXOffset = (text.length - i) * fontSizeInPx
+                assertThat(
+                    paragraph.getCursorRect(i),
+                    equalTo(Rect(
+                        left = cursorXOffset - cursorWidth / 2,
+                        top = 0f,
+                        right = cursorXOffset + cursorWidth / 2,
+                        bottom = fontSizeInPx
+                    ))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getCursorRect_rtl_multiLines() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2\u05D0\u05D1\u05D2"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+            val charsPerLine = 3
+
+            paragraph.layout(ParagraphConstraints(width = charsPerLine * fontSizeInPx))
+
+            for (i in 0 until charsPerLine) {
+                val cursorXOffset = (charsPerLine - i) * fontSizeInPx
+                assertThat(
+                    paragraph.getCursorRect(i),
+                    equalTo(Rect(
+                        left = cursorXOffset - cursorWidth / 2,
+                        top = 0f,
+                        right = cursorXOffset + cursorWidth / 2,
+                        bottom = fontSizeInPx
+                    ))
+                )
+            }
+
+            for (i in charsPerLine until text.length) {
+                val cursorXOffset = (charsPerLine - i % charsPerLine) * fontSizeInPx
+                assertThat(
+                    paragraph.getCursorRect(i),
+                    equalTo(Rect(
+                        left = cursorXOffset - cursorWidth / 2,
+                        top = fontSizeInPx,
+                        right = cursorXOffset + cursorWidth / 2,
+                        bottom = fontSizeInPx * 2.2f
+                    ))
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getCursorRect_rtl_newLine() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2\n\u05D0\u05D1\u05D2"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = 3 * fontSizeInPx))
+
+            // Cursor before '\n'
+            assertThat(
+                paragraph.getCursorRect(3),
+                equalTo(Rect(
+                    left = 0 - cursorWidth / 2,
+                    top = 0f,
+                    right = 0 + cursorWidth / 2,
+                    bottom = fontSizeInPx
+                ))
+            )
+
+            // Cursor after '\n'
+            assertThat(
+                paragraph.getCursorRect(4),
+                equalTo(Rect(
+                    left = 3 * fontSizeInPx - cursorWidth / 2,
+                    top = fontSizeInPx,
+                    right = 3 * fontSizeInPx + cursorWidth / 2,
+                    bottom = fontSizeInPx * 2.2f
+                ))
+            )
+        }
+    }
+
+    @Test
+    fun getCursorRect_rtl_newLine_last_char() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2\n"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = 3 * fontSizeInPx))
+
+            // Cursor before '\n'
+            assertThat(
+                paragraph.getCursorRect(3),
+                equalTo(Rect(
+                    left = 0 - cursorWidth / 2,
+                    top = 0f,
+                    right = 0 + cursorWidth / 2,
+                    bottom = fontSizeInPx
+                ))
+            )
+
+            // Cursor after '\n'
+            assertThat(
+                paragraph.getCursorRect(4),
+                equalTo(Rect(
+                    left = -cursorWidth / 2,
+                    top = fontSizeInPx,
+                    right = +cursorWidth / 2,
+                    bottom = fontSizeInPx * 2.2f
+                ))
+            )
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_ltr_singleLine_textDirectionDefault() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+
+            paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
+
+            for (i in 0..text.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(fontSizeInPx * i)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_rtl_singleLine_textDirectionDefault() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            for (i in 0..text.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(width - fontSizeInPx * i)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_bidi_singleLine_textDirectionDefault() {
+        withDensity(defaultDensity) {
+            val ltrText = "abc"
+            val rtlText = "\u05D0\u05D1\u05D2"
+            val text = ltrText + rtlText
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            for (i in 0..ltrText.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(fontSizeInPx * i)
+                )
+            }
+
+            for (i in 1 until rtlText.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i + ltrText.length),
+                    equalTo(width - fontSizeInPx * i)
+                )
+            }
+
+            assertThat(
+                paragraph.getPrimaryHorizontal(text.length),
+                equalTo(width)
+            )
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_ltr_singleLine_textDirectionRtl() {
+        withDensity(defaultDensity) {
+            val text = "abc"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(
+                text = text,
+                fontSize = fontSize,
+                textDirection = TextDirection.Rtl
+            )
+            val width = text.length * fontSizeInPx
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(0), equalTo(width))
+
+            for (i in 1 until text.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(fontSizeInPx * i)
+                )
+            }
+
+            assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_rtl_singleLine_textDirectionLtr() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(
+                text = text,
+                fontSize = fontSize,
+                textDirection = TextDirection.Ltr
+            )
+            val width = text.length * fontSizeInPx
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(0), equalTo(0f))
+
+            for (i in 1 until text.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(width - fontSizeInPx * i)
+                )
+            }
+
+            assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(width))
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_bidi_singleLine_textDirectionLtr() {
+        withDensity(defaultDensity) {
+            val ltrText = "abc"
+            val rtlText = "\u05D0\u05D1\u05D2"
+            val text = ltrText + rtlText
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(
+                text = text,
+                fontSize = fontSize,
+                textDirection = TextDirection.Ltr
+            )
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            for (i in 0..ltrText.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(fontSizeInPx * i)
+                )
+            }
+
+            for (i in 1 until rtlText.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i + ltrText.length),
+                    equalTo(width - fontSizeInPx * i)
+                )
+            }
+
+            assertThat(
+                paragraph.getPrimaryHorizontal(text.length),
+                equalTo(width)
+            )
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_bidi_singleLine_textDirectionRtl() {
+        withDensity(defaultDensity) {
+            val ltrText = "abc"
+            val rtlText = "\u05D0\u05D1\u05D2"
+            val text = ltrText + rtlText
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(
+                text = text,
+                fontSize = fontSize,
+                textDirection = TextDirection.Rtl
+            )
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(0), equalTo(width))
+            for (i in 1 until ltrText.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i),
+                    equalTo(rtlText.length * fontSizeInPx + i * fontSizeInPx)
+                )
+            }
+
+            for (i in 0..rtlText.length) {
+                assertThat(
+                    paragraph.getPrimaryHorizontal(i + ltrText.length),
+                    equalTo(rtlText.length * fontSizeInPx - i * fontSizeInPx)
+                )
+            }
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_ltr_newLine_textDirectionDefault() {
+        withDensity(defaultDensity) {
+            val text = "abc\n"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_rtl_newLine_textDirectionDefault() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2\n"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(text = text, fontSize = fontSize)
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_ltr_newLine_textDirectionRtl() {
+        withDensity(defaultDensity) {
+            val text = "abc\n"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(
+                text = text,
+                fontSize = fontSize,
+                textDirection = TextDirection.Rtl
+            )
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(width))
+        }
+    }
+
+    @Test
+    fun getPrimaryHorizontal_rtl_newLine_textDirectionLtr() {
+        withDensity(defaultDensity) {
+            val text = "\u05D0\u05D1\u05D2\n"
+            val fontSize = 50.sp
+            val fontSizeInPx = fontSize.toPx().value
+            val paragraph = simpleParagraph(
+                text = text,
+                fontSize = fontSize,
+                textDirection = TextDirection.Ltr
+            )
+            val width = text.length * fontSizeInPx
+
+            paragraph.layout(ParagraphConstraints(width))
+
+            assertThat(paragraph.getPrimaryHorizontal(text.length), equalTo(0f))
+        }
+    }
+
     @Test
     fun locale_withCJK_shouldNotDrawSame() {
         withDensity(defaultDensity) {
diff --git a/ui/ui-text/src/androidTest/java/androidx/ui/text/TextPainterIntegrationTest.kt b/ui/ui-text/src/androidTest/java/androidx/ui/text/TextPainterIntegrationTest.kt
index 41f1cf3..3b17aae 100644
--- a/ui/ui-text/src/androidTest/java/androidx/ui/text/TextPainterIntegrationTest.kt
+++ b/ui/ui-text/src/androidTest/java/androidx/ui/text/TextPainterIntegrationTest.kt
@@ -26,7 +26,6 @@
 import androidx.ui.core.px
 import androidx.ui.core.sp
 import androidx.ui.core.withDensity
-import androidx.ui.engine.geometry.Offset
 import androidx.ui.engine.geometry.Rect
 import androidx.ui.engine.geometry.Size
 import androidx.ui.text.FontTestData.Companion.BASIC_MEASURE_FONT
@@ -291,7 +290,7 @@
 
         textPainter.layout(Constraints(0.ipx, 20.ipx))
 
-        assertThat(textPainter.paragraph).isNotNull()
+        assertThat(textPainter.multiParagraph).isNotNull()
     }
 
     @Test
@@ -460,10 +459,10 @@
             val defaultSelectionColor = Color(0x6633B5E5)
             expectedPaint.color = defaultSelectionColor
 
-            val firstLineLeft = textPainter.paragraph?.getLineLeft(0)
-            val secondLineLeft = textPainter.paragraph?.getLineLeft(1)
-            val firstLineRight = textPainter.paragraph?.getLineRight(0)
-            val secondLineRight = textPainter.paragraph?.getLineRight(1)
+            val firstLineLeft = textPainter.multiParagraph?.getLineLeft(0)
+            val secondLineLeft = textPainter.multiParagraph?.getLineLeft(1)
+            val firstLineRight = textPainter.multiParagraph?.getLineRight(0)
+            val secondLineRight = textPainter.multiParagraph?.getLineRight(1)
             expectedCanvas.drawRect(
                 Rect(firstLineLeft!!, 0f, firstLineRight!!, fontSizeInPx),
                 expectedPaint
@@ -473,7 +472,7 @@
                     secondLineLeft!!,
                     fontSizeInPx,
                     secondLineRight!!,
-                    textPainter.paragraph!!.height
+                    textPainter.multiParagraph!!.height
                 ),
                 expectedPaint
             )
diff --git a/ui/ui-text/src/main/java/androidx/ui/input/EditProcessor.kt b/ui/ui-text/src/main/java/androidx/ui/input/EditProcessor.kt
index 0d8d5c0..d9367c9 100644
--- a/ui/ui-text/src/main/java/androidx/ui/input/EditProcessor.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/input/EditProcessor.kt
@@ -30,26 +30,26 @@
 class EditProcessor {
 
     // The previous editor state we passed back to the user of this class.
-    private var mPreviousState: EditorState? = null
+    private var mPreviousModel: EditorModel? = null
 
     // The editing buffer used for applying editor commands from IME.
     private var mBuffer: EditingBuffer =
         EditingBuffer(initialText = "", initialSelection = TextRange(0, 0))
 
     /**
-     * Must be called whenever new editor state arrives.
+     * Must be called whenever new editor model arrives.
      *
-     * This method updates the internal editing buffer with the given editor state.
+     * This method updates the internal editing buffer with the given editor model.
      * This method may tell the IME about the selection offset changes or extracted text changes.
      */
-    fun onNewState(state: EditorState, textInputService: TextInputService?) {
-        if (mPreviousState !== state) {
+    fun onNewState(model: EditorModel, textInputService: TextInputService?) {
+        if (mPreviousModel !== model) {
             mBuffer = EditingBuffer(
-                initialText = state.text,
-                initialSelection = state.selection)
+                initialText = model.text,
+                initialSelection = model.selection)
         }
 
-        textInputService?.onStateUpdated(state)
+        textInputService?.onStateUpdated(model)
     }
 
     /**
@@ -58,10 +58,10 @@
      * This method updates internal editing buffer with the given edit operations and returns the
      * latest editor state representation of the editing buffer.
      */
-    fun onEditCommands(ops: List<EditOperation>): EditorState {
+    fun onEditCommands(ops: List<EditOperation>): EditorModel {
         ops.forEach { it.process(mBuffer) }
 
-        val newState = EditorState(
+        val newState = EditorModel(
             text = mBuffer.toString(),
             selection = TextRange(mBuffer.selectionStart, mBuffer.selectionEnd),
             composition = if (mBuffer.hasComposition()) {
@@ -70,7 +70,7 @@
                 null
             })
 
-        mPreviousState = newState
+        mPreviousModel = newState
         return newState
     }
 }
\ No newline at end of file
diff --git a/ui/ui-text/src/main/java/androidx/ui/input/EditorState.kt b/ui/ui-text/src/main/java/androidx/ui/input/EditorModel.kt
similarity index 98%
rename from ui/ui-text/src/main/java/androidx/ui/input/EditorState.kt
rename to ui/ui-text/src/main/java/androidx/ui/input/EditorModel.kt
index b8e86aa..363e49a 100644
--- a/ui/ui-text/src/main/java/androidx/ui/input/EditorState.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/input/EditorModel.kt
@@ -26,7 +26,7 @@
  * state. TextInputService sends the latest editing state to TextInputClient when the platform input
  * service sends some input events.
  */
-class EditorState {
+class EditorModel {
 
     /**
      * The text
diff --git a/ui/ui-text/src/main/java/androidx/ui/input/TextInputService.kt b/ui/ui-text/src/main/java/androidx/ui/input/TextInputService.kt
index 382e110..c241f70 100644
--- a/ui/ui-text/src/main/java/androidx/ui/input/TextInputService.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/input/TextInputService.kt
@@ -29,7 +29,7 @@
      * Start text input session for given client.
      */
     fun startInput(
-        initState: EditorState,
+        initModel: EditorModel,
         keyboardType: KeyboardType,
         imeAction: ImeAction,
         onEditCommand: (List<EditOperation>) -> Unit,
@@ -49,9 +49,9 @@
     fun showSoftwareKeyboard()
 
     /*
-     * Notify the new editor state to IME.
+     * Notify the new editor model to IME.
      */
-    fun onStateUpdated(state: EditorState)
+    fun onStateUpdated(model: EditorModel)
 
     /**
      * Notify the focused rectangle to the system.
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/AnnotatedString.kt b/ui/ui-text/src/main/java/androidx/ui/text/AnnotatedString.kt
index 97fda37..28140c5 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/AnnotatedString.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/AnnotatedString.kt
@@ -21,8 +21,23 @@
  */
 data class AnnotatedString(
     val text: String,
-    val textStyles: List<Item<TextStyle>> = listOf()
+    val textStyles: List<Item<TextStyle>> = listOf(),
+    val paragraphStyles: List<Item<ParagraphStyle>> = listOf()
 ) {
+
+    init {
+        var lastStyleEnd = -1
+        for (paragraphStyle in paragraphStyles) {
+            if (paragraphStyle.start < lastStyleEnd) {
+                throw IllegalArgumentException("ParagraphStyle should not overlap")
+            }
+            if (paragraphStyle.end > text.length) {
+                throw IllegalArgumentException("ParagraphStyle range " +
+                        "[${paragraphStyle.start}, ${paragraphStyle.end}) is out of boundary")
+            }
+            lastStyleEnd = paragraphStyle.end
+        }
+    }
     /**
      * The information attached on the text such as a TextStyle.
      *
@@ -31,5 +46,11 @@
      * @param end The end of the range where [style] takes effect. It's exclusive.
      */
     // TODO(haoyuchang): Check some other naming options.
-    data class Item<T>(val style: T, val start: Int, val end: Int)
+    data class Item<T>(val style: T, val start: Int, val end: Int) {
+        init {
+            if (start > end) {
+                throw IllegalArgumentException("Reversed range is not supported")
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/MultiParagraph.kt b/ui/ui-text/src/main/java/androidx/ui/text/MultiParagraph.kt
new file mode 100644
index 0000000..830a776
--- /dev/null
+++ b/ui/ui-text/src/main/java/androidx/ui/text/MultiParagraph.kt
@@ -0,0 +1,634 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.text
+
+import androidx.ui.core.Density
+import androidx.ui.core.Px
+import androidx.ui.core.PxPosition
+import androidx.ui.core.px
+import androidx.ui.engine.geometry.Offset
+import androidx.ui.engine.geometry.Rect
+import androidx.ui.painting.Canvas
+import androidx.ui.painting.Path
+import androidx.ui.text.font.Font
+import java.lang.IllegalStateException
+import kotlin.math.max
+
+/**
+ * The class that renders multiple paragraphs at once.
+ *
+ * It's designed to support multiple [ParagraphStyle]s in single text widget.
+ */
+internal class MultiParagraph(
+    val annotatedString: AnnotatedString,
+    private val textStyle: TextStyle = TextStyle(),
+    private val paragraphStyle: ParagraphStyle = ParagraphStyle(),
+    val maxLines: Int? = null,
+    val ellipsis: Boolean? = null,
+    val density: Density,
+    val resourceLoader: Font.ResourceLoader
+) {
+    /**
+     * The minimum width that this paragraph could be without failing to paint
+     * its contents within itself.
+     *
+     * Valid only after [layout] has been called.
+     */
+    val minIntrinsicWidth: Float
+        get() = paragraphInfoList.foldRight(0f) { paragraphInfo, minWidth ->
+            max(paragraphInfo.paragraph.minIntrinsicWidth, minWidth)
+        }
+
+    /**
+     * Returns the smallest width beyond which increasing the width never
+     * decreases the height.
+     *
+     * Valid only after [layout] has been called.
+     */
+    val maxIntrinsicWidth: Float
+        get() = paragraphInfoList.foldRight(0f) { paragraphInfo, maxWidth ->
+            max(paragraphInfo.paragraph.maxIntrinsicWidth, maxWidth)
+        }
+
+    /**
+     * True if there is more vertical content, but the text was truncated, either
+     * because we reached `maxLines` lines of text or because the `maxLines` was
+     * null, `ellipsis` was not null, and one of the lines exceeded the width
+     * constraint.
+     *
+     * See the discussion of the `maxLines` and `ellipsis` arguments at [ParagraphStyle].
+     */
+    var didExceedMaxLines: Boolean = false
+        private set
+
+    /**
+     * The amount of horizontal space this paragraph occupies.
+     *
+     * Valid only after [layout] has been called.
+     */
+    var width: Float = 0f
+        private set
+
+    /**
+     * The amount of vertical space this paragraph occupies.
+     *
+     * Valid only after [layout] has been called.
+     */
+    var height: Float = 0f
+        private set
+
+    /**
+     * The distance from the top of the paragraph to the alphabetic
+     * baseline of the first line, in logical pixels.
+     */
+    val baseline: Float
+        get() = if (paragraphInfoList.isEmpty()) 0f else paragraphInfoList[0].paragraph.baseline
+
+    /** The total number of lines in the text. */
+    var lineCount: Int = 0
+        private set
+
+    private var needLayout = true
+
+    private val paragraphInfoList: List<ParagraphInfo>
+
+    init {
+        val paragraphStyles = fillInParagraphRanges(annotatedString, this.paragraphStyle)
+        this.paragraphInfoList = paragraphStyles.map { (style, start, end) ->
+            // TODO(haoyuchang): Change substring to Paragraph receiving text and range.
+            val textInParagraph = if (start != end) {
+                annotatedString.text.substring(start, end)
+            } else {
+                ""
+            }
+            val textStylesInParagraph = annotatedString.getLocalStyles(start, end)
+
+            // TODO(haoyuchang): remove the top and bottom padding between two paragraphs
+            val paragraph = Paragraph(
+                textInParagraph,
+                this.textStyle,
+                style,
+                textStylesInParagraph,
+                maxLines,
+                ellipsis,
+                density,
+                resourceLoader
+            )
+
+            ParagraphInfo(
+                paragraph = paragraph,
+                startIndex = start,
+                endIndex = end
+            )
+        }
+    }
+
+    /**
+     * Computes the size and position of each glyph in the paragraph.
+     *
+     * The [ParagraphConstraints] control how wide the text is allowed to be.
+     */
+    fun layout(constraints: ParagraphConstraints) {
+        this.needLayout = false
+        this.width = constraints.width
+        this.didExceedMaxLines = false
+
+        var currentLineCount = 0
+        var currentHeight = 0f
+
+        for ((index, paragraphInfo) in paragraphInfoList.withIndex()) {
+            val paragraph = paragraphInfo.paragraph
+            paragraph.layout(constraints)
+
+            paragraphInfo.startLineIndex = currentLineCount
+            paragraphInfo.endLineIndex = currentLineCount + paragraph.lineCount
+            currentLineCount = paragraphInfo.endLineIndex
+
+            paragraphInfo.top = currentHeight.px
+            paragraphInfo.bottom = (currentHeight + paragraph.height).px
+            currentHeight += paragraph.height
+
+            // TODO(haoyuchang): solve the corner case where the ellipsis won't be applied when
+            //  currentLineNum == maxLines but there are still more paragraphs
+            if (paragraph.didExceedMaxLines ||
+                (currentLineCount == maxLines && index != this.paragraphInfoList.lastIndex)
+            ) {
+                this.didExceedMaxLines = true
+                break
+            }
+        }
+        this.lineCount = currentLineCount
+        this.height = currentHeight
+    }
+
+    /** Paint the paragraphs to canvas. */
+    fun paint(canvas: Canvas) {
+        assertNeedLayout()
+
+        canvas.save()
+        paragraphInfoList.forEach {
+            it.paragraph.paint(canvas)
+            canvas.translate(0f, it.paragraph.height)
+        }
+        canvas.restore()
+    }
+
+    /** Returns path that enclose the given text range. */
+    fun getPathForRange(start: Int, end: Int): Path {
+        if (start !in 0..end || end > annotatedString.text.length) {
+            throw AssertionError(
+                "Start($start) or End($end) is out of range [0..${annotatedString.text.length})," +
+                        " or start > end!"
+            )
+        }
+        assertNeedLayout()
+
+        if (start == end) return Path()
+
+        val paragraphIndex = findParagraphByIndex(paragraphInfoList, start)
+        val path = Path()
+
+        paragraphInfoList.drop(paragraphIndex)
+            .takeWhile { it.startIndex < end }
+            .filterNot { it.startIndex == it.endIndex }
+            .forEach {
+                with(it) {
+                    path.addPath(
+                        path = paragraph.getPathForRange(
+                            start = start.toLocalIndex(),
+                            end = end.toLocalIndex()
+                        ).toGlobal()
+                    )
+                }
+            }
+        return path
+    }
+
+    /** Returns the character offset closest to the given graphical position. */
+    fun getOffsetForPosition(position: PxPosition): Int {
+        assertNeedLayout()
+        val paragraphIndex = when {
+            position.y.value <= 0f -> 0
+            position.y.value >= height -> paragraphInfoList.lastIndex
+            else -> findParagraphByY(paragraphInfoList, position.y)
+        }
+        return with(paragraphInfoList[paragraphIndex]) {
+            if (length == 0) {
+                max(0, startIndex - 1)
+            } else {
+                paragraph.getOffsetForPosition(position.toLocal()).toGlobalIndex()
+            }
+        }
+    }
+
+    /**
+     * Returns the bounding box as Rect of the character for given character offset. Rect
+     * includes the top, bottom, left and right of a character.
+     */
+    fun getBoundingBox(offset: Int): Rect {
+        assertNeedLayout()
+        assertIndexInRange(offset)
+
+        val paragraphIndex = findParagraphByIndex(paragraphInfoList, offset)
+        return with(paragraphInfoList[paragraphIndex]) {
+            val localOffset = offset.toLocalIndex()
+            paragraph.getBoundingBox(localOffset).toGlobal()
+        }
+    }
+
+    /** Get the primary horizontal position for the specified text offset. */
+    fun getPrimaryHorizontal(offset: Int): Float {
+        assertNeedLayout()
+        if (offset !in 0..annotatedString.text.length) {
+            throw AssertionError("offset($offset) is out of bounds " +
+                    "(0,${annotatedString.text.length}")
+        }
+
+        val paragraphIndex = if (offset == annotatedString.text.length) {
+            paragraphInfoList.lastIndex
+        } else {
+            findParagraphByIndex(paragraphInfoList, offset)
+        }
+
+        return with(paragraphInfoList[paragraphIndex]) {
+            paragraph.getPrimaryHorizontal(offset.toLocalIndex())
+        }
+    }
+
+    /**
+     * Returns the TextRange of the word at the given character offset. Characters not
+     * part of a word, such as spaces, symbols, and punctuation, have word breaks
+     * on both sides. In such cases, this method will return TextRange(offset, offset+1).
+     * Word boundaries are defined more precisely in Unicode Standard Annex #29
+     * http://www.unicode.org/reports/tr29/#Word_Boundaries
+     */
+    fun getWordBoundary(offset: Int): TextRange {
+        assertNeedLayout()
+        assertIndexInRange(offset)
+
+        val paragraphIndex = findParagraphByIndex(paragraphInfoList, offset)
+
+        return with(paragraphInfoList[paragraphIndex]) {
+            paragraph.getWordBoundary(offset.toLocalIndex()).toGlobal()
+        }
+    }
+
+    /** Returns rectangle of the cursor area. */
+    fun getCursorRect(offset: Int): Rect {
+        assertNeedLayout()
+        if (offset !in 0..annotatedString.text.length) {
+            throw AssertionError("offset($offset) is out of bounds " +
+                    "(0,${annotatedString.text.length}")
+        }
+
+        val paragraphIndex = if (offset == annotatedString.text.length) {
+            paragraphInfoList.lastIndex
+        } else {
+            findParagraphByIndex(paragraphInfoList, offset)
+        }
+
+        return with(paragraphInfoList[paragraphIndex]) {
+            paragraph.getCursorRect(offset.toLocalIndex()).toGlobal()
+        }
+    }
+
+    /**
+     * Returns the line number on which the specified text offset appears.
+     * If you ask for a position before 0, you get 0; if you ask for a position
+     * beyond the end of the text, you get the last line.
+     */
+    fun getLineForOffset(offset: Int): Int {
+        assertNeedLayout()
+        assertIndexInRange(offset)
+
+        val paragraphIndex = findParagraphByIndex(paragraphInfoList, offset)
+        return with(paragraphInfoList[paragraphIndex]) {
+            paragraph.getLineForOffset(offset.toLocalIndex()).toGlobalLineIndex()
+        }
+    }
+
+    /** Returns the left x Coordinate of the given line. */
+    fun getLineLeft(lineIndex: Int): Float {
+        assertNeedLayout()
+        assertLineIndexInRange(lineIndex)
+
+        val paragraphIndex = findParagraphByLineIndex(paragraphInfoList, lineIndex)
+
+        return with(paragraphInfoList[paragraphIndex]) {
+            paragraph.getLineLeft(lineIndex.toLocalLineIndex())
+        }
+    }
+
+    /** Returns the right x Coordinate of the given line. */
+    fun getLineRight(lineIndex: Int): Float {
+        assertNeedLayout()
+        assertLineIndexInRange(lineIndex)
+
+        val paragraphIndex = findParagraphByLineIndex(paragraphInfoList, lineIndex)
+
+        return with(paragraphInfoList[paragraphIndex]) {
+            paragraph.getLineRight(lineIndex.toLocalLineIndex())
+        }
+    }
+
+    /** Returns the bottom y coordinate of the given line. */
+    fun getLineBottom(lineIndex: Int): Float {
+        assertNeedLayout()
+        assertLineIndexInRange(lineIndex)
+
+        val paragraphIndex = findParagraphByLineIndex(paragraphInfoList, lineIndex)
+
+        return with(paragraphInfoList[paragraphIndex]) {
+            paragraph.getLineBottom(lineIndex.toLocalLineIndex())
+        }
+    }
+
+    /** Returns the height of the given line. */
+    fun getLineHeight(lineIndex: Int): Float {
+        assertNeedLayout()
+        assertLineIndexInRange(lineIndex)
+
+        val paragraphIndex = findParagraphByLineIndex(paragraphInfoList, lineIndex)
+
+        return with(paragraphInfoList[paragraphIndex]) {
+            paragraph.getLineHeight(lineIndex.toLocalLineIndex())
+        }
+    }
+
+    /** Returns the width of the given line. */
+    fun getLineWidth(lineIndex: Int): Float {
+        assertNeedLayout()
+        assertLineIndexInRange(lineIndex)
+
+        val paragraphIndex = findParagraphByLineIndex(paragraphInfoList, lineIndex)
+
+        return with(paragraphInfoList[paragraphIndex]) {
+            paragraph.getLineWidth(lineIndex.toLocalLineIndex())
+        }
+    }
+
+    private fun assertNeedLayout() {
+        if (needLayout) {
+            throw IllegalStateException("")
+        }
+    }
+
+    private fun assertIndexInRange(offset: Int) {
+        if (offset !in (0 until annotatedString.text.length)) {
+            throw IndexOutOfBoundsException("offset($offset) is out of bounds" +
+                    " [0, ${annotatedString.text.length})")
+        }
+    }
+
+    private fun assertLineIndexInRange(lineIndex: Int) {
+        if (lineIndex !in (0 until lineCount)) {
+            throw IndexOutOfBoundsException("lineIndex($lineIndex) is out of bounds" +
+                    " [0, $lineIndex)")
+        }
+    }
+}
+
+/**
+ * Given an character index of [MultiParagraph.annotatedString], find the corresponding
+ * [ParagraphInfo] which covers the provided index.
+ *
+ * @param paragraphInfoList The list of [ParagraphInfo] containing the information of each
+ *  paragraph in the [MultiParagraph].
+ * @param index The target index in the [MultiParagraph]. It should be in the range of
+ *  [0, text.length)
+ * @return The index of the target [ParagraphInfo] in [paragraphInfoList].
+ */
+private fun findParagraphByIndex(paragraphInfoList: List<ParagraphInfo>, index: Int): Int {
+    return paragraphInfoList.binarySearch { paragraphInfo ->
+        when {
+            paragraphInfo.startIndex > index -> 1
+            paragraphInfo.endIndex <= index -> -1
+            else -> 0
+        }
+    }
+}
+
+/**
+ * Given the y graphical position relative to this [MultiParagraph], find the index of the
+ * corresponding [ParagraphInfo] which occupies the provided position.
+ *
+ * @param paragraphInfoList The list of [ParagraphInfo] containing the information of each
+ *  paragraph in the [MultiParagraph].
+ * @param y The y coordinate position relative to the [MultiParagraph]. It should be in the range
+ *  of [0, [MultiParagraph.height]].
+ * @return The index of the target [ParagraphInfo] in [paragraphInfoList].
+ */
+private fun findParagraphByY(paragraphInfoList: List<ParagraphInfo>, y: Px): Int {
+    return paragraphInfoList.binarySearch { paragraphInfo ->
+        when {
+            paragraphInfo.top > y -> 1
+            paragraphInfo.bottom <= y -> -1
+            else -> 0
+        }
+    }
+}
+
+/**
+ * Given an line index in [MultiParagraph], find the corresponding [ParagraphInfo] which
+ * covers the provided line index.
+ *
+ * @param paragraphInfoList The list of [ParagraphInfo] containing the information of each
+ *  paragraph in the [MultiParagraph].
+ * @param lineIndex The target line index in the [MultiParagraph], it should be in the range of
+ *  [0, [MultiParagraph.lineCount])
+ * @return The index of the target [ParagraphInfo] in [paragraphInfoList].
+ */
+private fun findParagraphByLineIndex(paragraphInfoList: List<ParagraphInfo>, lineIndex: Int): Int {
+    return paragraphInfoList.binarySearch { paragraphInfo ->
+        when {
+            paragraphInfo.startLineIndex > lineIndex -> 1
+            paragraphInfo.endLineIndex <= lineIndex -> -1
+            else -> 0
+        }
+    }
+}
+
+/**
+ * A helper function used to determine the paragraph boundaries in [MultiParagraph].
+ *
+ * It reads paragraph information from [AnnotatedString.paragraphStyles] where only some parts of
+ * text has [ParagraphStyle] specified, and unspecified parts(gaps between specified paragraphs)
+ * are considered as default paragraph with default [ParagraphStyle].
+ * For example, the following string with a specified paragraph denoted by "[]"
+ *      "Hello WorldHi!"
+ *      [          ]
+ * The result paragraphs are "Hello World" and "Hi!".
+ *
+ * @param annotatedString: The [AnnotatedString] on which the paragraph boundaries need to be
+ *  determined.
+ * @param defaultParagraphStyle The default [ParagraphStyle]. It's used for both unspecified
+ *  default paragraphs and specified paragraph. When a specified paragraph's [ParagraphStyle] has
+ *  a null attribute, the default one will be used instead.
+ */
+internal fun fillInParagraphRanges(
+    annotatedString: AnnotatedString,
+    defaultParagraphStyle: ParagraphStyle
+): List<AnnotatedString.Item<ParagraphStyle>> {
+    val length = annotatedString.text.length
+    val paragraphStyles = annotatedString.paragraphStyles
+
+    var lastOffset = 0
+    val result = mutableListOf<AnnotatedString.Item<ParagraphStyle>>()
+    for ((style, start, end) in paragraphStyles) {
+        if (start != lastOffset) {
+            result.add(AnnotatedString.Item(defaultParagraphStyle, lastOffset, start))
+        }
+        result.add(AnnotatedString.Item(defaultParagraphStyle.merge(style), start, end))
+        lastOffset = end
+    }
+    if (lastOffset != length) {
+        result.add(AnnotatedString.Item(defaultParagraphStyle, lastOffset, length))
+    }
+    // This is a corner case where annotatedString is an empty string without any ParagraphStyle.
+    // In this case, a dummy ParagraphStyle is created.
+    if (result.isEmpty()) {
+        result.add(AnnotatedString.Item(defaultParagraphStyle, 0, 0))
+    }
+    return result
+}
+
+/**
+ * Helper function used to find the [TextStyle]s in the given paragraph range and also convert the
+ * range of those [TextStyle]s to paragraph local range.
+ *
+ * @param start The start index of the paragraph range, inclusive.
+ * @param end The end index of the paragraph range, exclusive.
+ * @return The list of converted [TextStyle]s in the given paragraph range.
+ */
+private fun AnnotatedString.getLocalStyles(
+    start: Int,
+    end: Int
+): List<AnnotatedString.Item<TextStyle>> {
+    if (start == end) {
+        return listOf()
+    }
+    // If the given range covers the whole AnnotatedString, return textStyles without conversion.
+    if (start == 0 && end >= this.text.length) {
+        return textStyles
+    }
+    return textStyles.filter { it.start < end && it.end > start }
+        .map {
+            AnnotatedString.Item(
+                it.style,
+                it.start.coerceIn(start, end) - start,
+                it.end.coerceIn(start, end) - start
+            )
+        }
+}
+
+/**
+ * This is a helper data structure to store the information of a single [Paragraph] in an
+ * [MultiParagraph]. It's mainly used to convert a global index, lineNumber and [Offset] to the
+ * local ones inside the [paragraph], and vice versa.
+ *
+ * @param paragraph The [Paragraph] object corresponding to this [ParagraphInfo].
+ * @param startIndex The start index of this paragraph in the parent [MultiParagraph], inclusive.
+ * @param endIndex The end index of this paragraph in the parent [MultiParagraph], exclusive.
+ * @param startLineIndex The start line index of this paragraph in the parent [MultiParagraph],
+ *  inclusive.
+ * @param endLineIndex The end line index of this paragraph in the parent [MultiParagraph],
+ *  exclusive.
+ * @param top The top position of the [paragraph] relative to the parent [MultiParagraph].
+ * @param bottom The bottom position of the [paragraph] relative to the parent [MultiParagraph].
+ */
+internal data class ParagraphInfo(
+    val paragraph: Paragraph,
+    val startIndex: Int,
+    val endIndex: Int,
+    var startLineIndex: Int = -1,
+    var endLineIndex: Int = -1,
+    var top: Px = (-1).px,
+    var bottom: Px = (-1).px
+) {
+
+    /**
+     * The length of the text in the covered by this paragraph.
+     */
+    val length
+        get() = endIndex - startIndex
+
+    /**
+     * Convert an index in the parent [MultiParagraph] to the local index in the [paragraph].
+     */
+    fun Int.toLocalIndex(): Int {
+        return this.coerceIn(startIndex, endIndex) - startIndex
+    }
+
+    /**
+     * Convert a local index in the [paragraph] to the global index in the parent [MultiParagraph].
+     */
+    fun Int.toGlobalIndex(): Int {
+        return this + startIndex
+    }
+
+    /**
+     * Convert a line index in the parent [MultiParagraph] to the local line index in the
+     * [paragraph].
+     *
+     */
+    fun Int.toLocalLineIndex(): Int {
+        return this - startLineIndex
+    }
+
+    /**
+     * Convert a local line index in the [paragraph] to the global line index in the parent
+     * [MultiParagraph].
+     */
+    fun Int.toGlobalLineIndex(): Int {
+        return this + startLineIndex
+    }
+
+    /**
+     * Convert a [PxPosition] relative to the parent [MultiParagraph] to the local [PxPosition]
+     * relative to the [paragraph].
+     */
+    fun PxPosition.toLocal(): PxPosition {
+        return copy(y = y - top)
+    }
+
+    /**
+     * Convert a [Rect] relative to the [paragraph] to the [Rect] relative to the parent
+     * [MultiParagraph].
+     */
+    fun Rect.toGlobal(): Rect {
+        return shift(Offset(dx = 0f, dy = this@ParagraphInfo.top.value))
+    }
+
+    /**
+     * Convert a [Path] relative to the [paragraph] to the [Path] relative to the parent
+     * [MultiParagraph].
+     *
+     * Notice that this function changes the input value.
+     */
+    fun Path.toGlobal(): Path {
+        shift(Offset(dx = 0f, dy = top.value))
+        return this
+    }
+
+    /**
+     * Convert a [TextRange] in to the [paragraph] to the [TextRange] in the parent
+     * [MultiParagraph].
+     */
+    fun TextRange.toGlobal(): TextRange {
+        return TextRange(start = start.toGlobalIndex(), end = end.toGlobalIndex())
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/ParagraphStyle.kt b/ui/ui-text/src/main/java/androidx/ui/text/ParagraphStyle.kt
index dd7c248..4fd974e 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/ParagraphStyle.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/ParagraphStyle.kt
@@ -47,19 +47,20 @@
         }
     }
     // TODO(siyamed) uncomment
-//    /**
-//     * Returns a new paragraph style that is a combination of this style and the given [other] style.
-//     *
-//     * If the given paragraph style is null, returns this paragraph style.
-//     */
-//    fun merge(other: ParagraphStyle? = null): ParagraphStyle {
-//        if (other == null) return this
-//
-//        return ParagraphStyle(
-//            lineHeight = other.lineHeight ?: this.lineHeight,
-//            textIndent = other.textIndent ?: this.textIndent,
-//            textAlign = other.textAlign ?: this.textAlign,
-//            textDirection = other.textDirection ?: this.textDirection
-//        )
-//    }
+    /**
+     * Returns a new paragraph style that is a combination of this style and the given [other]
+     * style.
+     *
+     * If the given paragraph style is null, returns this paragraph style.
+     */
+    fun merge(other: ParagraphStyle? = null): ParagraphStyle {
+        if (other == null) return this
+
+        return ParagraphStyle(
+            lineHeight = other.lineHeight ?: this.lineHeight,
+            textIndent = other.textIndent ?: this.textIndent,
+            textAlign = other.textAlign ?: this.textAlign,
+            textDirection = other.textDirection ?: this.textDirection
+        )
+    }
 }
\ No newline at end of file
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/TextPainter.kt b/ui/ui-text/src/main/java/androidx/ui/text/TextPainter.kt
index 7880c2d..0a5d93a 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/TextPainter.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/TextPainter.kt
@@ -118,7 +118,7 @@
     }
 
     @VisibleForTesting
-    internal var paragraph: Paragraph? = null
+    internal var multiParagraph: MultiParagraph? = null
         private set
 
     @VisibleForTesting
@@ -143,7 +143,7 @@
         set(value) {
             if (field == value) return
             field = value
-            paragraph = null
+            multiParagraph = null
             needsLayout = true
         }
 
@@ -219,7 +219,7 @@
     val minIntrinsicWidth: Float
         get() {
             assertNeedsLayout("minIntrinsicWidth")
-            return applyFloatingPointHack(paragraph!!.minIntrinsicWidth)
+            return applyFloatingPointHack(multiParagraph!!.minIntrinsicWidth)
         }
 
     /**
@@ -230,7 +230,7 @@
     val maxIntrinsicWidth: Float
         get() {
             assertNeedsLayout("maxIntrinsicWidth")
-            return applyFloatingPointHack(paragraph!!.maxIntrinsicWidth)
+            return applyFloatingPointHack(multiParagraph!!.maxIntrinsicWidth)
         }
 
     /**
@@ -281,7 +281,7 @@
     val didExceedMaxLines: Boolean
         get() {
             assertNeedsLayout("didExceedMaxLines")
-            return paragraph!!.didExceedMaxLines
+            return multiParagraph!!.didExceedMaxLines
         }
 
     /**
@@ -308,25 +308,25 @@
 
         if (!needsLayout && minWidth == lastMinWidth && finalMaxWidth == lastMaxWidth) return
         needsLayout = false
-        if (paragraph == null) {
-            paragraph = Paragraph(
-                text = text!!.text,
-                style = createTextStyle(),
-                paragraphStyle = createParagraphStyle(),
-                textStyles = text!!.textStyles,
-                maxLines = maxLines,
-                ellipsis = isEllipsis,
-                density = density,
-                resourceLoader = resourceLoader
+
+        if (multiParagraph == null) {
+            multiParagraph = MultiParagraph(
+                text!!,
+                createTextStyle(),
+                paragraphStyle ?: ParagraphStyle(),
+                maxLines,
+                isEllipsis,
+                density,
+                resourceLoader
             )
         }
         lastMinWidth = minWidth
         lastMaxWidth = finalMaxWidth
-        paragraph!!.layout(ParagraphConstraints(width = finalMaxWidth))
+        multiParagraph!!.layout(ParagraphConstraints(width = finalMaxWidth))
         if (minWidth != finalMaxWidth) {
             val newWidth = maxIntrinsicWidth.coerceIn(minWidth, finalMaxWidth)
-            if (newWidth != paragraph!!.width) {
-                paragraph!!.layout(ParagraphConstraints(width = newWidth))
+            if (newWidth != multiParagraph!!.width) {
+                multiParagraph!!.layout(ParagraphConstraints(width = newWidth))
             }
         }
     }
@@ -336,11 +336,11 @@
 
         val didOverflowHeight = didExceedMaxLines
         size = constraints.constrain(
-            IntPxSize(paragraph!!.width.px.round(), paragraph!!.height.px.round())
+            IntPxSize(multiParagraph!!.width.px.round(), multiParagraph!!.height.px.round())
         ).let {
             Size(it.width.value.toFloat(), it.height.value.toFloat())
         }
-        val didOverflowWidth = size.width < paragraph!!.width
+        val didOverflowWidth = size.width < multiParagraph!!.width
         // TODO(abarth): We're only measuring the sizes of the line boxes here. If
         // the glyphs draw outside the line boxes, we might think that there isn't
         // visual overflow when there actually is visual overflow. This can become
@@ -356,8 +356,8 @@
                 resourceLoader = resourceLoader
             )
             fadeSizePainter.layoutText()
-            val fadeWidth = fadeSizePainter.paragraph!!.width
-            val fadeHeight = fadeSizePainter.paragraph!!.height
+            val fadeWidth = fadeSizePainter.multiParagraph!!.width
+            val fadeHeight = fadeSizePainter.multiParagraph!!.height
             if (didOverflowWidth) {
                 val (fadeStart, fadeEnd) = if (textDirection == TextDirection.Rtl) {
                     Pair(fadeWidth, 0.0f)
@@ -426,7 +426,8 @@
             }
             canvas.clipRect(bounds)
         }
-        paragraph!!.paint(canvas)
+
+        multiParagraph!!.paint(canvas)
         if (hasVisualOverflow) {
             if (overflowShader != null) {
                 val bounds = Rect.fromLTWH(0f, 0f, size.width, size.height)
@@ -452,7 +453,7 @@
     fun paintBackground(start: Int, end: Int, color: Color, canvas: Canvas) {
         assert(!needsLayout)
         if (start == end) return
-        val selectionPath = paragraph!!.getPathForRange(start, end)
+        val selectionPath = multiParagraph!!.getPathForRange(start, end)
         // TODO(haoyuchang): check if move this paint to parameter is better
         canvas.drawPath(selectionPath, Paint().apply { this.color = color })
     }
@@ -467,7 +468,7 @@
      */
     fun paintCursor(offset: Int, canvas: Canvas) {
         assert(!needsLayout)
-        val cursorRect = paragraph!!.getCursorRect(offset)
+        val cursorRect = multiParagraph!!.getCursorRect(offset)
         canvas.drawRect(cursorRect, Paint().apply { this.color = Color.Black })
     }
 
@@ -479,7 +480,7 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     fun getLineBottom(lineIndex: Int): Float {
         assert(!needsLayout)
-        return paragraph!!.getLineBottom(lineIndex)
+        return multiParagraph!!.getLineBottom(lineIndex)
     }
 
     /**
@@ -492,7 +493,7 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     fun getLineForOffset(offset: Int): Int {
         assert(!needsLayout)
-        return paragraph!!.getLineForOffset(offset)
+        return multiParagraph!!.getLineForOffset(offset)
     }
 
     /**
@@ -503,13 +504,13 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     fun getPrimaryHorizontal(offset: Int): Float {
         assert(!needsLayout)
-        return paragraph!!.getPrimaryHorizontal(offset)
+        return multiParagraph!!.getPrimaryHorizontal(offset)
     }
 
     /** Returns the character offset closest to the given graphical position. */
     fun getOffsetForPosition(position: PxPosition): Int {
         assert(!needsLayout)
-        return paragraph!!.getOffsetForPosition(position)
+        return multiParagraph!!.getOffsetForPosition(position)
     }
 
     /**
@@ -523,7 +524,7 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY)
     fun getBoundingBox(offset: Int): Rect {
         assert(!needsLayout)
-        return paragraph!!.getBoundingBox(offset)
+        return multiParagraph!!.getBoundingBox(offset)
     }
 
     /**
@@ -536,6 +537,6 @@
      */
     fun getWordBoundary(offset: Int): TextRange {
         assert(!needsLayout)
-        return paragraph!!.getWordBoundary(offset)
+        return multiParagraph!!.getWordBoundary(offset)
     }
 }
diff --git a/ui/ui-text/src/test/java/androidx/ui/input/EditProcessorTest.kt b/ui/ui-text/src/test/java/androidx/ui/input/EditProcessorTest.kt
index 3bdaf143..591e2c4 100644
--- a/ui/ui-text/src/test/java/androidx/ui/input/EditProcessorTest.kt
+++ b/ui/ui-text/src/test/java/androidx/ui/input/EditProcessorTest.kt
@@ -39,8 +39,8 @@
         val proc = EditProcessor()
         val tis: TextInputService = mock()
 
-        proc.onNewState(EditorState("ABCDE", TextRange(0, 0)), tis)
-        val captor = argumentCaptor<EditorState>()
+        proc.onNewState(EditorModel("ABCDE", TextRange(0, 0)), tis)
+        val captor = argumentCaptor<EditorModel>()
         verify(tis, times(1)).onStateUpdated(captor.capture())
         assertEquals(1, captor.allValues.size)
         assertEquals("ABCDE", captor.firstValue.text)
diff --git a/ui/ui-text/src/test/java/androidx/ui/text/MultiParagraphTest.kt b/ui/ui-text/src/test/java/androidx/ui/text/MultiParagraphTest.kt
new file mode 100644
index 0000000..ce472ad
--- /dev/null
+++ b/ui/ui-text/src/test/java/androidx/ui/text/MultiParagraphTest.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.text
+
+import androidx.ui.text.style.TextAlign
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class MultiParagraphTest {
+    @Test
+    fun `test fillInParagraphRanges`() {
+        val text = "Hello World"
+        val paragraphStyle = ParagraphStyle(textAlign = TextAlign.Center)
+        val paragraphStyles = listOf(AnnotatedString.Item(paragraphStyle, 0, 5))
+        val annotatedString = AnnotatedString(text = text, paragraphStyles = paragraphStyles)
+        val defaultParagraphStyle = ParagraphStyle(lineHeight = 2.0f)
+
+        val paragraphs = fillInParagraphRanges(annotatedString, defaultParagraphStyle)
+
+        assertThat(paragraphs.size).isEqualTo(2)
+
+        assertThat(paragraphs[0].style).isEqualTo(defaultParagraphStyle.merge(paragraphStyle))
+        assertThat(paragraphs[0].start).isEqualTo(0)
+        assertThat(paragraphs[0].end).isEqualTo(5)
+
+        assertThat(paragraphs[1].style).isEqualTo(defaultParagraphStyle)
+        assertThat(paragraphs[1].start).isEqualTo(5)
+        assertThat(paragraphs[1].end).isEqualTo(text.length)
+    }
+
+    @Test
+    fun `test fillInParagraphRanges only string`() {
+        val text = "Hello World"
+        val annotatedString = AnnotatedString(text = text)
+        val defaultParagraphStyle = ParagraphStyle(lineHeight = 2.0f)
+
+        val paragraphs = fillInParagraphRanges(annotatedString, defaultParagraphStyle)
+
+        assertThat(paragraphs.size).isEqualTo(1)
+
+        assertThat(paragraphs[0].style).isEqualTo(defaultParagraphStyle)
+        assertThat(paragraphs[0].start).isEqualTo(0)
+        assertThat(paragraphs[0].end).isEqualTo(text.length)
+    }
+
+    @Test
+    fun `test fillInParagraphRanges empty string`() {
+        val text = ""
+        val annotatedString = AnnotatedString(text = text)
+        val defaultParagraphStyle = ParagraphStyle(lineHeight = 2.0f)
+
+        val paragraphs = fillInParagraphRanges(annotatedString, defaultParagraphStyle)
+
+        assertThat(paragraphs.size).isEqualTo(1)
+
+        assertThat(paragraphs[0].style).isEqualTo(defaultParagraphStyle)
+        assertThat(paragraphs[0].start).isEqualTo(0)
+        assertThat(paragraphs[0].end).isEqualTo(text.length)
+    }
+
+    @Test
+    fun `test fillInParagraphRanges with newLine`() {
+        val text = "Hello\nWorld"
+        val annotatedString = AnnotatedString(text = text)
+        val defaultParagraphStyle = ParagraphStyle(lineHeight = 2.0f)
+
+        val paragraphs = fillInParagraphRanges(annotatedString, defaultParagraphStyle)
+
+        assertThat(paragraphs.size).isEqualTo(1)
+
+        assertThat(paragraphs[0].style).isEqualTo(defaultParagraphStyle)
+        assertThat(paragraphs[0].start).isEqualTo(0)
+        assertThat(paragraphs[0].end).isEqualTo(text.length)
+    }
+
+    @Test
+    fun `test fillInParagraphRanges with only lineFeed`() {
+        val text = "\n"
+        val annotatedString = AnnotatedString(text = text)
+        val defaultParagraphStyle = ParagraphStyle(lineHeight = 2.0f)
+
+        val paragraphs = fillInParagraphRanges(annotatedString, defaultParagraphStyle)
+
+        assertThat(paragraphs.size).isEqualTo(1)
+
+        assertThat(paragraphs[0].style).isEqualTo(defaultParagraphStyle)
+        assertThat(paragraphs[0].start).isEqualTo(0)
+        assertThat(paragraphs[0].end).isEqualTo(1)
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-text/src/test/java/androidx/ui/text/TextPainterTest.kt b/ui/ui-text/src/test/java/androidx/ui/text/TextPainterTest.kt
index 6c07a79..5dcae69 100644
--- a/ui/ui-text/src/test/java/androidx/ui/text/TextPainterTest.kt
+++ b/ui/ui-text/src/test/java/androidx/ui/text/TextPainterTest.kt
@@ -127,7 +127,7 @@
         textPainter.text = text
 
         assertThat(textPainter.text).isEqualTo(text)
-        assertThat(textPainter.paragraph).isNull()
+        assertThat(textPainter.multiParagraph).isNull()
         assertThat(textPainter.needsLayout).isTrue()
     }
 
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AccessibilityTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AccessibilityTest.kt
index 050aab8..4736f91 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AccessibilityTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AccessibilityTest.kt
@@ -97,7 +97,7 @@
             assertBasicState(initialPage)
 
             var node = AccessibilityNodeInfoCompat.obtain()
-            activityTestRule.runOnUiThread {
+            runOnUiThreadSync {
                 ViewCompat.onInitializeAccessibilityNodeInfo(viewPager, node)
             }
             var collectionInfo = node.collectionInfo
@@ -127,7 +127,7 @@
             val initialPage = viewPager.currentItem
             assertBasicState(initialPage)
 
-            activityTestRule.runOnUiThread {
+            runOnUiThreadSync {
                 viewPager.setOrientation(getOppositeOrientation(config.orientation))
             }
             assertBasicState(initialPage)
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeTest.kt
index face96e..8d4bc8f 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterDataSetChangeTest.kt
@@ -67,7 +67,7 @@
 
         // Dispatch and wait for data set changes
         val layoutChangedLatch = test.viewPager.addWaitForLayoutChangeLatch()
-        activityTestRule.runOnUiThread {
+        test.runOnUiThreadSync {
             config.actions.forEach { it.perform(adapter) }
         }
         layoutChangedLatch.await(1, SECONDS)
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterTest.kt
index fc705e5..e696227 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/AdapterTest.kt
@@ -18,8 +18,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
-import androidx.testutils.PollingCheck
-import androidx.testutils.waitForExecution
 import androidx.viewpager2.widget.AdapterTest.Event.OnPageScrollStateChangedEvent
 import androidx.viewpager2.widget.AdapterTest.Event.OnPageScrolledEvent
 import androidx.viewpager2.widget.AdapterTest.Event.OnPageSelectedEvent
@@ -33,7 +31,6 @@
 import org.junit.Assert.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit.SECONDS
 
 @LargeTest
@@ -56,7 +53,7 @@
         test.assertBasicState(0)
         test.viewPager.setCurrentItemSync(1, false, 2, SECONDS)
         test.assertBasicState(1)
-        activityTestRule.runOnUiThread {
+        test.runOnUiThreadSync {
             test.viewPager.adapter = test.viewPager.adapter
         }
         test.assertBasicState(0)
@@ -211,7 +208,7 @@
 
     private fun clearDataSet() {
         assertThat(dataSet.size, greaterThan(0))
-        modifyDataSet {
+        test.modifyDataSetSync {
             val itemCount = dataSet.size
             dataSet.clear()
             test.viewPager.adapter!!.notifyItemRangeRemoved(0, itemCount)
@@ -221,35 +218,13 @@
 
     private fun fillDataSet() {
         assertThat(dataSet.size, equalTo(0))
-        modifyDataSet {
+        test.modifyDataSetSync {
             dataSet.addAll(stringSequence(pageCount))
             test.viewPager.adapter!!.notifyItemRangeInserted(0, pageCount)
         }
         test.assertBasicState(0)
     }
 
-    private fun modifyDataSet(block: () -> Unit) {
-        val layoutChangedLatch = test.viewPager.addWaitForLayoutChangeLatch()
-        activityTestRule.runOnUiThread {
-            block()
-        }
-        layoutChangedLatch.await(1, SECONDS)
-
-        // Let animations run
-        val animationLatch = CountDownLatch(1)
-        test.viewPager.recyclerView.itemAnimator!!.isRunning {
-            animationLatch.countDown()
-        }
-        animationLatch.await(1, SECONDS)
-
-        // Wait until VP2 has stabilized
-        activityTestRule.waitForExecution()
-        val adapter = test.viewPager.adapter
-        if (adapter != null && adapter.itemCount > 0) {
-            PollingCheck.waitFor(1000) { test.viewPager.currentCompletelyVisibleItem != -1 }
-        }
-    }
-
     private fun ViewPager2.addNewRecordingCallback(): RecordingCallback {
         return RecordingCallback().also { registerOnPageChangeCallback(it) }
     }
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
index 679943f..1a284f4 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
@@ -146,7 +146,20 @@
                 field = value
             }
 
-        fun runOnUiThread(f: () -> Unit) = activity.runOnUiThread(f)
+        fun runOnUiThreadSync(f: () -> Unit) {
+            var thrownError: Throwable? = null
+            activityTestRule.runOnUiThread {
+                try {
+                    f()
+                } catch (t: Throwable) {
+                    thrownError = t
+                }
+            }
+            val caughtError = thrownError
+            if (caughtError != null) {
+                throw caughtError
+            }
+        }
 
         val viewPager: ViewPager2 get() = activity.findViewById(R.id.view_pager)
 
@@ -302,7 +315,7 @@
             )
 
             var node = AccessibilityNodeInfo.obtain()
-            activityTestRule.runOnUiThread { viewPager.onInitializeAccessibilityNodeInfo(node) }
+            runOnUiThreadSync { viewPager.onInitializeAccessibilityNodeInfo(node) }
             @Suppress("DEPRECATION") var standardActions = node.actions
 
             assertThat("scroll backward action expected: $expectScrollBackwardAction",
@@ -374,7 +387,7 @@
     fun Context.setAdapterSync(adapterProvider: AdapterProvider) {
         lateinit var waitForRenderLatch: CountDownLatch
 
-        activityTestRule.runOnUiThread {
+        runOnUiThreadSync {
             waitForRenderLatch = viewPager.addWaitForLayoutChangeLatch()
             viewPager.adapter = adapterProvider(activity)
         }
@@ -443,6 +456,9 @@
         return latch
     }
 
+    val ViewPager2.linearLayoutManager: LinearLayoutManager
+        get() = recyclerView.layoutManager as LinearLayoutManager
+
     val ViewPager2.recyclerView: RecyclerView
         get() {
             return getChildAt(0) as RecyclerView
@@ -452,8 +468,7 @@
         get() {
             var position = RecyclerView.NO_POSITION
             activityTestRule.runOnUiThread {
-                position = (recyclerView.layoutManager as LinearLayoutManager)
-                    .findFirstCompletelyVisibleItemPosition()
+                position = linearLayoutManager.findFirstCompletelyVisibleItemPosition()
             }
             return position
         }
@@ -488,6 +503,21 @@
         assertPageActions()
     }
 
+    fun Context.modifyDataSetSync(block: () -> Unit) {
+        val layoutChangedLatch = viewPager.addWaitForLayoutChangeLatch()
+        runOnUiThreadSync {
+            block()
+        }
+        layoutChangedLatch.await(1, TimeUnit.SECONDS)
+
+        // Let animations run
+        val animationLatch = CountDownLatch(1)
+        viewPager.recyclerView.itemAnimator!!.isRunning {
+            animationLatch.countDown()
+        }
+        animationLatch.await(1, TimeUnit.SECONDS)
+    }
+
     fun ViewPager2.setCurrentItemSync(
         targetPage: Int,
         smoothScroll: Boolean,
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DragWhileSmoothScrollTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DragWhileSmoothScrollTest.kt
index 1282878..ea4bcf4 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DragWhileSmoothScrollTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/DragWhileSmoothScrollTest.kt
@@ -17,7 +17,6 @@
 package androidx.viewpager2.widget
 
 import android.widget.TextView
-import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.test.filters.LargeTest
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.testutils.SwipeToLocation.flingToCenter
@@ -83,7 +82,7 @@
         // when we are close enough
         val waitTillCloseEnough = test.viewPager.addWaitForDistanceToTarget(config.targetPage,
             config.distanceToTargetWhenStartDrag)
-        test.runOnUiThread { test.viewPager.setCurrentItem(config.targetPage, true) }
+        test.runOnUiThreadSync { test.viewPager.setCurrentItem(config.targetPage, true) }
         waitTillCloseEnough.await(2, SECONDS)
 
         // then perform a swipe
@@ -158,8 +157,8 @@
         // Find the view on the UI thread, as RV may be in layout
         val pageText = "$pageToSnapTo"
         var viewFound = false
-        test.activityTestRule.runOnUiThread {
-            val llm = test.viewPager.recyclerView.layoutManager as LinearLayoutManager
+        test.runOnUiThreadSync {
+            val llm = test.viewPager.linearLayoutManager
             var i = 0
             while (!viewFound && i < llm.childCount) {
                 val view = llm.getChildAt(i++) as TextView
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
index 874da2c..4a8f194 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
@@ -322,7 +322,7 @@
     @SdkSuppress(minSdkVersion = 16)
     fun test_performA11yActionDuringFakeDrag() {
         startManualDragDuringFakeDrag(.9f, 1000, interpolator = fastDecelerateInterpolator) {
-            activityTestRule.runOnUiThread {
+            test.runOnUiThreadSync {
                 ViewCompat.performAccessibilityAction(test.viewPager, getNextPageAction(), null)
             }
         }
@@ -410,12 +410,12 @@
 
             // start smooth scroll
             val scrollLatch = test.viewPager.addWaitForFirstScrollEventLatch()
-            test.runOnUiThread { test.viewPager.setCurrentItem(settleTarget, true) }
+            test.runOnUiThreadSync { test.viewPager.setCurrentItem(settleTarget, true) }
             assertThat(scrollLatch.await(2, SECONDS), equalTo(true))
 
             // start fake drag, but check some preconditions first
             var idleLatch: CountDownLatch? = null
-            activityTestRule.runOnUiThread {
+            test.runOnUiThreadSync {
                 val dragDistance = dragDistanceCallback()
                 val currPosition = test.viewPager.relativeScrollPosition
 
@@ -481,18 +481,14 @@
     }
 
     private fun doIllegalAction(errorMessage: String, action: () -> Unit) {
-        val executionLatch = CountDownLatch(1)
         var exception: IllegalStateException? = null
-        test.runOnUiThread {
+        test.runOnUiThreadSync {
             try {
                 action()
             } catch (e: IllegalStateException) {
                 exception = e
-            } finally {
-                executionLatch.countDown()
             }
         }
-        assertThat(executionLatch.await(1, SECONDS), equalTo(true))
         assertThat(exception, notNullValue())
         assertThat(exception!!.message, equalTo(errorMessage))
     }
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentLifecycleTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentLifecycleTest.kt
index 99e6f4e..51f84084 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentLifecycleTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FragmentLifecycleTest.kt
@@ -184,7 +184,7 @@
 
                 // wait for layout to finish after data-set change
                 val latchLayout = viewPager.addWaitForLayoutChangeLatch()
-                activityTestRule.runOnUiThread(it.dataChangeAction)
+                runOnUiThreadSync(it.dataChangeAction)
                 latchLayout.await(timeoutMs, MILLISECONDS)
 
                 // wait for animations to finish after data-set change
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/MutableCollectionsTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/MutableCollectionsTest.kt
index 1e98bbf..ee8c165 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/MutableCollectionsTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/MutableCollectionsTest.kt
@@ -110,7 +110,7 @@
     }
 
     private fun Context.setCurrentPageContent(newContent: String) {
-        runOnUiThread {
+        runOnUiThreadSync {
             PageView.setPageText(PageView.findPageInActivity(activity)!!, newContent)
         }
     }
@@ -144,7 +144,7 @@
         val latch = CountDownLatch(1)
         viewPager.viewTreeObserver.addOnGlobalLayoutListener { latch.countDown() }
 
-        runOnUiThread { viewPager.adapter!!.notifyDataSetChanged() }
+        runOnUiThreadSync { viewPager.adapter!!.notifyDataSetChanged() }
         latch.await(5, TimeUnit.SECONDS)
     }
 
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt
index 18a3c27..a19d995 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt
@@ -78,7 +78,7 @@
     @LargeTest
     fun test() {
         test = setUpTest(config.orientation)
-        activityTestRule.runOnUiThread {
+        test.runOnUiThreadSync {
             test.viewPager.offscreenPageLimit = config.offscreenPageLimit
         }
         val recorder = test.viewPager.addNewRecordingCallback()
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
index 81b6edd..d8afaac 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
@@ -441,7 +441,7 @@
             val latch = viewPager.addWaitForScrolledLatch(targetPages.last(), true)
 
             // when
-            runOnUiThread {
+            runOnUiThreadSync {
                 targetPages.forEach {
                     viewPager.setCurrentItem(it, true)
                 }
@@ -493,9 +493,9 @@
             val idleLatch = viewPager.addWaitForIdleLatch()
 
             // when
-            runOnUiThread { viewPager.setCurrentItem(targetPage, true) }
+            runOnUiThreadSync { viewPager.setCurrentItem(targetPage, true) }
             scrollLatch.await(2, SECONDS)
-            runOnUiThread {
+            runOnUiThreadSync {
                 viewPager.setCurrentItem(targetPage, false)
                 callback.markEvent(marker)
             }
@@ -609,7 +609,7 @@
             val callback = viewPager.addNewRecordingCallback()
 
             // when
-            runOnUiThread { viewPager.setCurrentItem(targetPage, true) }
+            runOnUiThreadSync { viewPager.setCurrentItem(targetPage, true) }
             delayCallback(viewPager)
 
             recreateActivity(adapterProvider) { newViewPager ->
@@ -718,7 +718,7 @@
             pageSwiper.swipeForward(halfPage + 2 * touchSlop, AccelerateInterpolator())
             settleLatch.await(2, SECONDS)
             var scrollLatch: CountDownLatch? = null
-            activityTestRule.runOnUiThread {
+            test.runOnUiThreadSync {
                 scrollLatch = test.viewPager.addWaitForFirstScrollEventLatch()
             }
             scrollLatch!!.await(2, SECONDS)
@@ -774,7 +774,7 @@
 
             // when
             listOf(2, 2, 0, 0, 1, 2, 1, 0).forEach { targetPage ->
-                runOnUiThread { viewPager.setCurrentItem(targetPage, smoothScroll) }
+                runOnUiThreadSync { viewPager.setCurrentItem(targetPage, smoothScroll) }
 
                 // poll the viewpager on the ui thread
                 viewPager.waitUntilSnappedOnTargetByPolling(targetPage)
@@ -816,7 +816,7 @@
 
         // Test SCROLL_STATE_SETTLING
         test_getScrollState(test, SCROLL_STATE_SETTLING, 1) {
-            test.runOnUiThread { test.viewPager.setCurrentItem(1, true) }
+            test.runOnUiThreadSync { test.viewPager.setCurrentItem(1, true) }
         }
 
         // Test SCROLL_STATE_DRAGGING (real drag)
@@ -1020,7 +1020,7 @@
 
         val recorder = test.viewPager.addNewRecordingCallback()
         val distanceLatch = test.viewPager.addWaitForDistanceToTarget(targetPage, 1.5f)
-        test.runOnUiThread {
+        test.runOnUiThreadSync {
             test.viewPager.setCurrentItem(targetPage, true)
         }
 
@@ -1032,6 +1032,99 @@
         recorder.assertAllPagesSelected(listOf(targetPage, targetPage + 1))
     }
 
+    @Test
+    fun test_removeFirstVisibleItemWhileScrolling_targetNotBound() {
+        test_removeFirstVisibleItemWhileScrolling(false)
+    }
+
+    @Test
+    fun test_removeFirstVisibleItemWhileScrolling_targetBound() {
+        test_removeFirstVisibleItemWhileScrolling(true)
+    }
+
+    /** @param targetBound If the target page should be bound by RV when modifying the dataset */
+    private fun test_removeFirstVisibleItemWhileScrolling(targetBound: Boolean) {
+        // given
+        val pageCount = 10     // number of pages
+        val initialPage = 0    // page we start at
+        val targetPage = 5     // page we smooth scroll to
+        val removeItemMark = 1 // id of the mark we make when modifying the dataset
+        val bindThreshold = 2  // how many pages from x before x gets bound?
+        val epsilon = 0.001f
+        // start and end of the window of opportunity to modify the dataset
+        val windowStart = targetPage - bindThreshold - if (targetBound) 0 else 1
+        val windowEnd = targetPage - if (targetBound) 0 else bindThreshold
+
+        val test = setUpTest(config.orientation)
+        activityTestRule.runOnUiThread { test.viewPager.offscreenPageLimit = 1 }
+        val dataSet = stringSequence(pageCount).toMutableList()
+        test.setAdapterSync(viewAdapterProvider(dataSet))
+
+        tryNTimes(3, resetBlock = {
+            test.viewPager.setCurrentItemSync(initialPage, false, 2, SECONDS)
+            // VP2 was potentially settling while the RetryException was raised,
+            // in which case we must wait until the IDLE event has been fired
+            activityTestRule.waitForExecution(1)
+        }) {
+            // when we are scrolling to the target
+            val recorder = test.viewPager.addNewRecordingCallback()
+            val distanceLatch = test.viewPager.addWaitForDistanceToTarget(targetPage,
+                targetPage - windowStart - epsilon)
+            val idleLatch = test.viewPager.addWaitForIdleLatch()
+            test.runOnUiThreadSync { test.viewPager.setCurrentItem(targetPage, true) }
+            distanceLatch.await(2, SECONDS)
+
+            // and we remove the first visible item
+            test.modifyDataSetSync {
+                val lastScrollPosition = recorder.scrollEvents.last().let {
+                    it.position + it.positionOffset.toDouble()
+                }
+                if (lastScrollPosition >= windowEnd) {
+                    throw RetryException("Data set should be modified while scrolling through " +
+                            "($windowStart, $windowEnd), but was modified at $lastScrollPosition")
+                }
+                recorder.markEvent(removeItemMark)
+                val position = test.viewPager.linearLayoutManager.findFirstVisibleItemPosition()
+                dataSet.removeAt(position)
+                test.viewPager.adapter!!.notifyItemRemoved(position)
+            }
+            idleLatch.await(2, SECONDS)
+
+            // then
+            val expectedFinalPage = targetPage - if (targetBound) 1 else 0
+            val expectedFinalPageText = expectedFinalPage + 1
+            test.assertBasicState(expectedFinalPage, "$expectedFinalPageText")
+            recorder.apply {
+                val removeItemMarkIx = markerIx(removeItemMark)
+                val expectedSelectEvents = listOf(targetPage).plus(
+                    if (targetPage != expectedFinalPage) listOf(expectedFinalPage) else emptyList()
+                )
+                // verify all events
+                assertThat(settlingIx, equalTo(0))
+                assertThat(pageSelectedIx(targetPage), equalTo(1))
+                assertThat(removeItemMarkIx, greaterThan(1))
+                assertThat(idleIx, equalTo(lastIx))
+                assertThat(selectEvents.map { it.position }, equalTo(expectedSelectEvents))
+                assertThat(scrollEventCount, equalTo(eventCount - 3 - expectedSelectEvents.size))
+
+                // verify scroll events _before_ and _after_ the marker
+                val scrollsBeforeMarker = scrollEventsBefore(removeItemMarkIx)
+                val scrollsAfterMarker = scrollEventsAfter(removeItemMarkIx)
+                listOf(scrollsBeforeMarker, scrollsAfterMarker).forEach {
+                    it.assertPositionSorted(SortOrder.ASC)
+                    it.assertOffsetSorted(SortOrder.ASC)
+                    it.assertValueSanity(0, targetPage, test.viewPager.pageSize)
+                }
+                // Only check assertMaxShownPages on scroll events _before_ the marker:
+                //   after the data set change, it can scroll an arbitrary number of pages
+                scrollsBeforeMarker.assertMaxShownPages()
+                // Only check assertLastCorrect on scroll events _after_ the marker:
+                //   the target is not reached before the data set change
+                scrollsAfterMarker.assertLastCorrect(expectedFinalPage)
+            }
+        }
+    }
+
     private fun ViewPager2.addNewRecordingCallback(): RecordingCallback {
         return RecordingCallback().also { registerOnPageChangeCallback(it) }
     }
@@ -1058,11 +1151,8 @@
 
         val allEvents get() = events
         val scrollEvents get() = events.mapNotNull { it as? OnPageScrolledEvent }
-        val scrollEventsBeforeSettling
-            get() = events.subList(0, settlingIx).mapNotNull { it as? OnPageScrolledEvent }
-        val scrollEventsAfterSettling
-            get() = events.subList(settlingIx + 1, events.size)
-                    .mapNotNull { it as? OnPageScrolledEvent }
+        val scrollEventsBeforeSettling get() = scrollEventsBefore(settlingIx)
+        val scrollEventsAfterSettling get() = scrollEventsAfter(settlingIx)
         val selectEvents get() = events.mapNotNull { it as? OnPageSelectedEvent }
         val stateEvents get() = events.mapNotNull { it as? OnPageScrollStateChangedEvent }
         val scrollAndSelectEvents get() = events.mapNotNull {
@@ -1080,6 +1170,15 @@
         val draggingIx get() = events.indexOf(OnPageScrollStateChangedEvent(SCROLL_STATE_DRAGGING))
         val idleIx get() = events.indexOf(OnPageScrollStateChangedEvent(SCROLL_STATE_IDLE))
         val pageSelectedIx: (page: Int) -> Int = { events.indexOf(OnPageSelectedEvent(it)) }
+        val markerIx: (id: Int) -> Int = { events.indexOf(MarkerEvent(it)) }
+
+        val scrollEventsBefore: (ix: Int) -> List<OnPageScrolledEvent> =
+            { scrollEventsBetween(0, it) }
+        val scrollEventsAfter: (ix: Int) -> List<OnPageScrolledEvent> =
+            { scrollEventsBetween(it + 1, events.size) }
+        val scrollEventsBetween: (fromIx: Int, toIx: Int) -> List<OnPageScrolledEvent> = { a, b ->
+            events.subList(a, b).mapNotNull { it as? OnPageScrolledEvent }
+        }
 
         val wasSettleInterrupted: Boolean get() {
             val changeToSettlingEvent = OnPageScrollStateChangedEvent(SCROLL_STATE_SETTLING)
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageFillTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageFillTest.kt
index a14a337..75c233e 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageFillTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageFillTest.kt
@@ -66,7 +66,7 @@
         }
 
         setUpTest(ORIENTATION_HORIZONTAL).apply {
-            runOnUiThread {
+            runOnUiThreadSync {
                 viewPager.adapter = fixedViewSizeAdapter
                 try {
                     viewPager.measure(0, 0)
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt
index 5981d6b..4d78da2 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt
@@ -180,8 +180,7 @@
     }
 
     private fun ViewPager2.addNewRecordingCallback(): RecordingCallback {
-        val layoutManager = recyclerView.layoutManager as LinearLayoutManager
-        return RecordingCallback(layoutManager).also {
+        return RecordingCallback(linearLayoutManager).also {
             setPageTransformer(it)
             registerOnPageChangeCallback(it)
         }
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt
index 3f4e84f..8d6159b 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt
@@ -144,7 +144,7 @@
 
                 // when
                 pageSequence.forEachIndexed { i, targetPage ->
-                    runOnUiThread {
+                    runOnUiThreadSync {
                         viewPager.setCurrentItem(targetPage, i !in instantScrolls)
                         viewPager.assertCurrentItemSet(targetPage)
                         if (currentPage != targetPage) {
@@ -345,6 +345,13 @@
             totalPages = 12,
             pageSequence = listOf(8, 7, 9, 7, 3, 0, 7, 11, 10, 0),
             instantScrolls = setOf(1, 4, 5, 8, 9)
+        ),
+        TestConfig(
+            title = "smooth-instant-long_smooth",
+            orientation = orientation,
+            totalPages = 5,
+            pageSequence = listOf(3, 4, 0),
+            instantScrolls = setOf(1)
         )
     )
     .plus(
@@ -396,4 +403,4 @@
     return (0 until seqLen).filter { r.nextFloat() < probability }.toSet()
 }
 
-// endregion
\ No newline at end of file
+// endregion
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SwipeTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SwipeTest.kt
index bc81524..9cacdcd 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SwipeTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SwipeTest.kt
@@ -51,7 +51,7 @@
                     val modifiedPageValue: String? = stepToNewValue[currentStep]
                     if (modifiedPageValue != null) {
                         expectedValues[currentPage] = modifiedPageValue
-                        runOnUiThread {
+                        runOnUiThreadSync {
                             PageView.setPageText(PageView.findPageInActivity(activity)!!,
                                     modifiedPageValue)
                         }
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt
index 4a3337c..ebe5a6a 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt
@@ -22,6 +22,7 @@
 import androidx.fragment.app.FragmentManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
 import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -40,6 +41,7 @@
     private val timeoutMs = 3000L
 
     @Test
+    @SdkSuppress(minSdkVersion = 16) /** [View.setHasTransientState] was introduced in API 16 */
     fun test_swipeBetweenPages() {
         setUpTest(orientation).apply {
             val expectedValues = stringSequence(totalPages)
diff --git a/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java b/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
index a07954e..53ddc92 100644
--- a/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
+++ b/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
@@ -60,8 +60,7 @@
  * 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
+ * <li>{@link RecyclerView.Adapter#onViewRecycled} we remove, save state, destroy the
  * {@link Fragment}.
  * </ul>
  */
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java b/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java
index f5e9736..fa9f235 100644
--- a/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java
+++ b/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java
@@ -125,10 +125,10 @@
         }
 
         // Drag is finished (dragging || settling -> idle)
-        if (isInAnyDraggingState() && newState == RecyclerView.SCROLL_STATE_IDLE) {
+        if (mAdapterState != STATE_IDLE && newState == RecyclerView.SCROLL_STATE_IDLE) {
             boolean dispatchIdle = false;
             updateScrollEventValues();
-            if (!mScrollHappened) {
+            if (!mScrollHappened && isInAnyDraggingState()) {
                 // Pages didn't move during drag, so either we're at the start or end of the list,
                 // or there are no pages at all.
                 // In the first case, ViewPager's contract requires at least one scroll event.
@@ -137,11 +137,19 @@
                     dispatchScrolled(mScrollValues.mPosition, 0f, 0);
                 }
                 dispatchIdle = true;
-            } else if (mScrollValues.mOffsetPx == 0) {
-                // Normally we dispatch the selected page and go to idle in onScrolled when
-                // mOffsetPx == 0, but in this case the drag was still ongoing when onScrolled was
-                // called, so that didn't happen. And since mOffsetPx == 0, there will be no further
-                // scroll events, so fire the onPageSelected event and go to idle now.
+            } else if (mScrollHappened && mScrollValues.mOffsetPx == 0) {
+                // Normally we dispatch the selected page and go to idle in onScrolled after we
+                // settled (mOffsetPx == 0), but there are a few exceptions:
+                //
+                // 1) The drag was still ongoing when onScrolled was called, so we didn't know it
+                //    was about to end. End it now.
+                // 2) If the adapter data set changes during a smooth scroll, RecyclerView may
+                //    settle at a different position, which we don't know about until we're there.
+                //    End it now.
+                //
+                // Now RecyclerView is idle and mOffsetPx == 0, so the view has stabilized. Fire
+                // onPageSelected to notify clients of the position settled upon and go to idle.
+                //
                 // Note that if we _did_ go to idle in that last onScrolled event, this code will
                 // not be executed because mAdapterState has been reset to STATE_IDLE.
                 dispatchIdle = true;
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
index 5b9f51e..7c71ca7 100644
--- a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
+++ b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
@@ -512,17 +512,7 @@
                 mTmpChildRect.bottom);
 
         if (mCurrentItemDirty) {
-            RecyclerView.ItemAnimator animator = mRecyclerView.getItemAnimator();
-            if (animator == null) {
-                updateCurrentItem();
-            } else {
-                animator.isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
-                    @Override
-                    public void onAnimationsFinished() {
-                        updateCurrentItem();
-                    }
-                });
-            }
+            updateCurrentItem();
         }
     }
 
diff --git a/webkit/integration-tests/testapp/src/main/AndroidManifest.xml b/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
index b999b64..c827c61 100644
--- a/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -83,6 +83,9 @@
             android:name=".AssetLoaderAjaxActivity"
             android:exported="true" />
         <activity
+            android:name=".AssetLoaderInternalStorageActivity"
+            android:exported="true" />
+        <activity
             android:name=".ForceDarkActivity"
             android:theme="@style/AppDayNightTheme"
             android:exported="true" />
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderInternalStorageActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderInternalStorageActivity.java
new file mode 100644
index 0000000..a62d043
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderInternalStorageActivity.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.androidx.webkit;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebResourceResponse;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.webkit.WebViewAssetLoader;
+import androidx.webkit.WebViewAssetLoader.InternalStoragePathHandler;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * An {@link Activity} to show case a use case of using {@link InternalStoragePathHandler}.
+ */
+public class AssetLoaderInternalStorageActivity extends AppCompatActivity {
+    private static final String DEMO_HTML_CONTENT =
+            "<h3>Successfully loaded html from app files dir!</h3>";
+
+    @NonNull private File mPublicDir;
+    @NonNull private File mDemoFile;
+
+    @NonNull private WebViewAssetLoader mAssetLoader;
+    @NonNull private WebView mWebView;
+
+    private class MyWebViewClient extends WebViewClient {
+        @Override
+        public boolean shouldOverrideUrlLoading(WebView view, String url) {
+            return false;
+        }
+
+        @Override
+        @RequiresApi(21)
+        public WebResourceResponse shouldInterceptRequest(WebView view,
+                                            WebResourceRequest request) {
+            return mAssetLoader.shouldInterceptRequest(request.getUrl());
+        }
+
+        @Override
+        public WebResourceResponse shouldInterceptRequest(WebView view, String request) {
+            return mAssetLoader.shouldInterceptRequest(Uri.parse(request));
+        }
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.activity_asset_loader);
+        setTitle(R.string.asset_loader_internal_storage_activity_title);
+        WebkitHelpers.appendWebViewVersionToTitle(this);
+
+        mWebView = findViewById(R.id.webview_asset_loader_webview);
+        mWebView.setWebViewClient(new MyWebViewClient());
+
+        mPublicDir = new File(getFilesDir(), "public");
+        mDemoFile = new File(mPublicDir, "some_text.html");
+
+        // Host "files/public/" in app's data directory under:
+        // http://appassets.androidplatform.net/public_data/...
+        mAssetLoader = new WebViewAssetLoader.Builder()
+                .addPathHandler("/public_data/", new InternalStoragePathHandler(this, mPublicDir))
+                .build();
+
+        // Write the demo file asynchronously and then load the file after it's written.
+        new WriteFileTask(mDemoFile, DEMO_HTML_CONTENT) {
+            @Override
+            protected void onPostExecute(Void result) {
+                Uri path = new Uri.Builder()
+                        .scheme("https")
+                        .authority(WebViewAssetLoader.DEFAULT_DOMAIN)
+                        .appendPath("public_data")
+                        .appendPath("some_text.html")
+                        .build();
+
+                mWebView.loadUrl(path.toString());
+            }
+        }.execute();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        // Clean the test/demo file for tests.
+        mDemoFile.delete();
+        mPublicDir.delete();
+    }
+
+    // Writes to file asynchronously in the background thread.
+    private class WriteFileTask extends AsyncTask<Void, Void, Void> {
+        @NonNull private final File mFile;
+        @NonNull private final String mContent;
+
+        WriteFileTask(@NonNull File file, @NonNull String content) {
+            mFile = file;
+            mContent = content;
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            mFile.getParentFile().mkdirs();
+            try (FileOutputStream fos = new FileOutputStream(mFile)) {
+                fos.write(mContent.getBytes("utf-8"));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+            return null;
+        }
+
+    }
+}
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderListActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderListActivity.java
index 24628b15..e07539e 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderListActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderListActivity.java
@@ -49,6 +49,9 @@
                 new MenuListView.MenuItem(
                     getResources().getString(R.string.asset_loader_ajax_activity_title),
                     new Intent(activityContext, AssetLoaderAjaxActivity.class)),
+                new MenuListView.MenuItem(
+                    getResources().getString(R.string.asset_loader_internal_storage_activity_title),
+                    new Intent(activityContext, AssetLoaderInternalStorageActivity.class)),
         };
         listView.setItems(menuItems);
     }
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/ForceDarkActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/ForceDarkActivity.java
index c2c6aea..aa8531b 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/ForceDarkActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/ForceDarkActivity.java
@@ -16,7 +16,6 @@
 
 package com.example.androidx.webkit;
 
-import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.os.Bundle;
 import android.util.Base64;
@@ -42,7 +41,6 @@
             "<html><body><h1>Force Dark Mode is %s </h1></body></html>";
 
     @Override
-    @SuppressLint("RestrictedApi")
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
@@ -62,7 +60,6 @@
         }
     }
 
-    @SuppressLint("RestrictedApi")
     private void setupWebView(WebView webView, int forceDarkMode) {
         webView.setWebViewClient(new WebViewClient());
         String formatedDescription;
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MultiProcessEnabledActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MultiProcessEnabledActivity.java
index 9a13e2f..ae365cd 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MultiProcessEnabledActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MultiProcessEnabledActivity.java
@@ -16,7 +16,6 @@
 
 package com.example.androidx.webkit;
 
-import android.annotation.SuppressLint;
 import android.os.Bundle;
 
 import androidx.appcompat.app.AppCompatActivity;
@@ -29,7 +28,6 @@
 public class MultiProcessEnabledActivity extends AppCompatActivity {
 
     @Override
-    @SuppressLint("RestrictedApi")
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_is_multi_process_enabled);
diff --git a/webkit/integration-tests/testapp/src/main/res/values/strings.xml b/webkit/integration-tests/testapp/src/main/res/values/strings.xml
index 881793a..13c5706 100644
--- a/webkit/integration-tests/testapp/src/main/res/values/strings.xml
+++ b/webkit/integration-tests/testapp/src/main/res/values/strings.xml
@@ -22,6 +22,7 @@
     <string name="not_updateable_webview">not updatable</string>
     <string name="proxy_override_requests_served">Requests served: %d</string>
     <string name="asset_loader_ajax_activity_title">Asset Loader AJAX Demo</string>
+    <string name="asset_loader_internal_storage_activity_title">Load from internal storage</string>
     <string name="asset_loader_list_activity_title">WebView Asset Loader Demos</string>
     <string name="asset_loader_simple_activity_title">Simple Asset Loader</string>
     <string name="small_interstitial_activity_title">Small Interstitial</string>
diff --git a/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java b/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
index c4540e0..62f2a98 100644
--- a/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
+++ b/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
@@ -293,7 +293,7 @@
      * TODO(amalova): unhide
      * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public static final int FORCE_DARK_OFF = 0;
 
     /**
@@ -307,7 +307,7 @@
      * TODO(amalova): unhide
      * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public static final int FORCE_DARK_AUTO = 1;
 
     /**
@@ -320,7 +320,7 @@
      * TODO(amalova): unhide
      * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public static final int FORCE_DARK_ON = 2;
 
     /**
@@ -350,7 +350,7 @@
      * TODO(amalova): unhide
      * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @SuppressLint("NewApi")
     @RequiresFeature(name = WebViewFeature.FORCE_DARK,
             enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
@@ -383,7 +383,7 @@
      * TODO(amalova): unhide
      * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @SuppressLint("NewApi")
     @RequiresFeature(name = WebViewFeature.FORCE_DARK,
             enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
diff --git a/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java b/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
index b34d682..f782bcb 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
@@ -234,7 +234,7 @@
      * @hide
      */
     // TODO(b/132880733) unhide the API when it's ready.
-    @RestrictTo(Scope.LIBRARY_GROUP_PREFIX)
+    @RestrictTo(Scope.LIBRARY_GROUP)
     public static final class InternalStoragePathHandler implements PathHandler {
         /**
          * Forbidden subdirectories of {@link Context#getDataDir} that cannot be exposed by this
diff --git a/webkit/src/main/java/androidx/webkit/WebViewCompat.java b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
index e021a09..565d36b 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewCompat.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
@@ -622,7 +622,7 @@
      * //TODO(laisminchillo): unhide
      * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @RequiresFeature(name = WebViewFeature.MULTI_PROCESS_QUERY,
             enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public static boolean isMultiProcessEnabled() {
diff --git a/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index 5994789..f71c46d 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -391,7 +391,7 @@
      * TODO(cricke): unhide
      * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public static final String SUPPRESS_ERROR_PAGE = "SUPPRESS_ERROR_PAGE";
 
     /**
@@ -401,7 +401,7 @@
      * TODO(laisminchillo): unhide
      * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public static final String MULTI_PROCESS_QUERY = "MULTI_PROCESS_QUERY";
 
     /**
@@ -413,7 +413,7 @@
      * TODO(amalova): unhide
      * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public static final String FORCE_DARK = "FORCE_DARK";
 
     /**
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcherTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcherTest.java
index 620307f..95b6d4c 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcherTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcherTest.java
@@ -567,6 +567,7 @@
 
     @Test
     public void testConstraintsChanged_withFutureWork() throws InterruptedException {
+        when(mBatteryChargingTracker.getInitialState()).thenReturn(true);
         // Use a mocked scheduler in this test.
         Scheduler scheduler = mock(Scheduler.class);
         doCallRealMethod().when(mWorkManager).rescheduleEligibleWork();
diff --git a/work/workmanager/src/main/java/androidx/work/ListenableWorker.java b/work/workmanager/src/main/java/androidx/work/ListenableWorker.java
index 2203618..41ac5d0 100644
--- a/work/workmanager/src/main/java/androidx/work/ListenableWorker.java
+++ b/work/workmanager/src/main/java/androidx/work/ListenableWorker.java
@@ -180,6 +180,9 @@
      * A ListenableWorker is given a maximum of ten minutes to finish its execution and return a
      * {@link Result}.  After this time has expired, the worker will be signalled to stop and its
      * {@link ListenableFuture} will be cancelled.
+     * <p>
+     * The future will also be cancelled if this worker is stopped for any reason
+     * (see {@link #onStopped()}).
      *
      * @return A {@link ListenableFuture} with the {@link Result} of the computation.  If you
      *         cancel this Future, WorkManager will treat this unit of work as failed.
@@ -222,12 +225,13 @@
     }
 
     /**
-     * This method is invoked when this Worker has been told to stop.  This could happen due
-     * to an explicit cancellation signal by the user, or because the system has decided to preempt
-     * the task.  In these cases, the results of the work will be ignored by WorkManager.  All
-     * processing in this method should be lightweight - there are no contractual guarantees about
-     * which thread will invoke this call, so this should not be a long-running or blocking
-     * operation.
+     * This method is invoked when this Worker has been told to stop.  At this point, the
+     * {@link ListenableFuture} returned by the instance of {@link #startWork()} is
+     * also cancelled.  This could happen due to an explicit cancellation signal by the user, or
+     * because the system has decided to preempt the task.  In these cases, the results of the
+     * work will be ignored by WorkManager.  All processing in this method should be lightweight
+     * - there are no contractual guarantees about which thread will invoke this call, so this
+     * should not be a long-running or blocking operation.
      */
     public void onStopped() {
         // Do nothing by default.