Merge "Fixed lint warnings" into androidx-master-dev
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 1ecc48eb..d522e82 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -1,5 +1,6 @@
 <component name="ProjectCodeStyleConfiguration">
   <code_scheme name="Project" version="173">
+    <option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
     <JavaCodeStyleSettings>
       <option name="FIELD_NAME_PREFIX" value="m" />
       <option name="STATIC_FIELD_NAME_PREFIX" value="s" />
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 174d43b..f4c5c4f 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -51,4 +51,4 @@
   <component name="ProjectType">
     <option name="id" value="Android" />
   </component>
-</project>
\ No newline at end of file
+</project>
diff --git a/README.md b/README.md
index 96b9476..552466f 100644
--- a/README.md
+++ b/README.md
@@ -84,9 +84,9 @@
 
     ./gradlew createArchive
 
-And put in your **project** `build.gradle` file:
+And put the following at the top of your 'repositories' property in your **project** `build.gradle` file:
 
-    handler.maven { url '/path/to/checkout/out/androidx/build/support_repo/' }
+    maven { url '/path/to/checkout/out/androidx/build/support_repo/' }
 
 ### Continuous integration
 [Our continuous integration system](https://ci.android.com/builds/branches/aosp-androidx-master-dev/grid?) builds all in progress (and potentially unstable) libraries as new changes are merged. You can manually download these AARs and JARs for your experimentation.
diff --git a/activity/activity-ktx/src/main/java/androidx/activity/ActivityViewModelLazy.kt b/activity/activity-ktx/src/main/java/androidx/activity/ActivityViewModelLazy.kt
index 2b7b97f..0cdc8cc 100644
--- a/activity/activity-ktx/src/main/java/androidx/activity/ActivityViewModelLazy.kt
+++ b/activity/activity-ktx/src/main/java/androidx/activity/ActivityViewModelLazy.kt
@@ -20,7 +20,6 @@
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelLazy
 import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory
 import androidx.lifecycle.ViewModelProvider.Factory
 
 /**
@@ -42,10 +41,7 @@
     noinline factoryProducer: (() -> Factory)? = null
 ): Lazy<VM> {
     val factoryPromise = factoryProducer ?: {
-        val application = application ?: throw IllegalArgumentException(
-            "ViewModel can be accessed only when Activity is attached"
-        )
-        AndroidViewModelFactory.getInstance(application)
+        defaultViewModelProviderFactory
     }
 
     return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
diff --git a/activity/activity/api/1.1.0-alpha02.txt b/activity/activity/api/1.1.0-alpha02.txt
index cd98da6..f087fd3 100644
--- a/activity/activity/api/1.1.0-alpha02.txt
+++ b/activity/activity/api/1.1.0-alpha02.txt
@@ -1,9 +1,10 @@
 // Signature format: 3.0
 package androidx.activity {
 
-  public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+  public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
     ctor public ComponentActivity();
     ctor @ContentView public ComponentActivity(@LayoutRes int);
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
     method @Deprecated public Object? getLastCustomNonConfigurationInstance();
     method public androidx.lifecycle.Lifecycle getLifecycle();
     method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
diff --git a/activity/activity/api/current.txt b/activity/activity/api/current.txt
index cd98da6..f087fd3 100644
--- a/activity/activity/api/current.txt
+++ b/activity/activity/api/current.txt
@@ -1,9 +1,10 @@
 // Signature format: 3.0
 package androidx.activity {
 
-  public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+  public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
     ctor public ComponentActivity();
     ctor @ContentView public ComponentActivity(@LayoutRes int);
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
     method @Deprecated public Object? getLastCustomNonConfigurationInstance();
     method public androidx.lifecycle.Lifecycle getLifecycle();
     method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
diff --git a/activity/activity/api/restricted_1.1.0-alpha02.txt b/activity/activity/api/restricted_1.1.0-alpha02.txt
index cd98da6..f087fd3 100644
--- a/activity/activity/api/restricted_1.1.0-alpha02.txt
+++ b/activity/activity/api/restricted_1.1.0-alpha02.txt
@@ -1,9 +1,10 @@
 // Signature format: 3.0
 package androidx.activity {
 
-  public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+  public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
     ctor public ComponentActivity();
     ctor @ContentView public ComponentActivity(@LayoutRes int);
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
     method @Deprecated public Object? getLastCustomNonConfigurationInstance();
     method public androidx.lifecycle.Lifecycle getLifecycle();
     method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
diff --git a/activity/activity/api/restricted_current.txt b/activity/activity/api/restricted_current.txt
index cd98da6..f087fd3 100644
--- a/activity/activity/api/restricted_current.txt
+++ b/activity/activity/api/restricted_current.txt
@@ -1,9 +1,10 @@
 // Signature format: 3.0
 package androidx.activity {
 
-  public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
+  public class ComponentActivity extends androidx.core.app.ComponentActivity implements androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.activity.OnBackPressedDispatcherOwner androidx.savedstate.SavedStateRegistryOwner androidx.lifecycle.ViewModelStoreOwner {
     ctor public ComponentActivity();
     ctor @ContentView public ComponentActivity(@LayoutRes int);
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
     method @Deprecated public Object? getLastCustomNonConfigurationInstance();
     method public androidx.lifecycle.Lifecycle getLifecycle();
     method public final androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
diff --git a/activity/activity/build.gradle b/activity/activity/build.gradle
index 81bb273..36959fc 100644
--- a/activity/activity/build.gradle
+++ b/activity/activity/build.gradle
@@ -18,10 +18,11 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     api(project(":lifecycle:lifecycle-runtime"))
     api(project(":lifecycle:lifecycle-viewmodel"))
     api("androidx.savedstate:savedstate:1.0.0-rc01")
+    api(project(":lifecycle:lifecycle-viewmodel-savedstate"))
 
     androidTestImplementation(KOTLIN_STDLIB)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityViewModelTest.kt b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityViewModelTest.kt
index b7a8b3f..e52b140 100644
--- a/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityViewModelTest.kt
+++ b/activity/activity/src/androidTest/java/androidx/activity/ComponentActivityViewModelTest.kt
@@ -16,7 +16,10 @@
 
 package androidx.activity
 
+import android.app.Application
 import android.os.Bundle
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.SavedStateHandle
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelStore
@@ -72,12 +75,18 @@
     fun testActivityOnCleared() {
         lateinit var activityModel: TestViewModel
         lateinit var defaultActivityModel: TestViewModel
+        lateinit var androidModel: TestAndroidViewModel
+        lateinit var savedStateModel: TestSavedStateViewModel
         ActivityScenario.launch(ViewModelActivity::class.java).use { scenario ->
             activityModel = scenario.withActivity { this.activityModel }
             defaultActivityModel = scenario.withActivity { this.defaultActivityModel }
+            androidModel = scenario.withActivity { this.androidModel }
+            savedStateModel = scenario.withActivity { this.savedStateModel }
         }
         assertThat(activityModel.cleared).isTrue()
         assertThat(defaultActivityModel.cleared).isTrue()
+        assertThat(androidModel.cleared).isTrue()
+        assertThat(savedStateModel.cleared).isTrue()
     }
 }
 
@@ -91,18 +100,19 @@
     lateinit var postOnCreateViewModelStore: ViewModelStore
     lateinit var activityModel: TestViewModel
     lateinit var defaultActivityModel: TestViewModel
+    lateinit var androidModel: TestAndroidViewModel
+    lateinit var savedStateModel: TestSavedStateViewModel
 
     override fun onCreate(savedInstanceState: Bundle?) {
         preOnCreateViewModelStore = viewModelStore
         super.onCreate(savedInstanceState)
         postOnCreateViewModelStore = viewModelStore
 
-        val viewModelProvider = ViewModelProvider(
-            this,
-            ViewModelProvider.NewInstanceFactory()
-        )
+        val viewModelProvider = ViewModelProvider(this)
         activityModel = viewModelProvider.get(KEY_ACTIVITY_MODEL, TestViewModel::class.java)
         defaultActivityModel = viewModelProvider.get(TestViewModel::class.java)
+        androidModel = viewModelProvider.get(TestAndroidViewModel::class.java)
+        savedStateModel = viewModelProvider.get(TestSavedStateViewModel::class.java)
     }
 }
 
@@ -112,4 +122,21 @@
     override fun onCleared() {
         cleared = true
     }
-}
\ No newline at end of file
+}
+
+class TestAndroidViewModel(application: Application) : AndroidViewModel(application) {
+    var cleared = false
+
+    override fun onCleared() {
+        cleared = true
+    }
+}
+
+@Suppress("unused")
+class TestSavedStateViewModel(val savedStateHandle: SavedStateHandle) : ViewModel() {
+    var cleared = false
+
+    override fun onCleared() {
+        cleared = true
+    }
+}
diff --git a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
index a596e73..cee2d27 100644
--- a/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
+++ b/activity/activity/src/main/java/androidx/activity/ComponentActivity.java
@@ -29,11 +29,14 @@
 import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.lifecycle.HasDefaultViewModelProviderFactory;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleEventObserver;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
 import androidx.lifecycle.ReportFragment;
+import androidx.lifecycle.SavedStateViewModelFactory;
+import androidx.lifecycle.ViewModelProvider;
 import androidx.lifecycle.ViewModelStore;
 import androidx.lifecycle.ViewModelStoreOwner;
 import androidx.savedstate.SavedStateRegistry;
@@ -50,6 +53,7 @@
 public class ComponentActivity extends androidx.core.app.ComponentActivity implements
         LifecycleOwner,
         ViewModelStoreOwner,
+        HasDefaultViewModelProviderFactory,
         SavedStateRegistryOwner,
         OnBackPressedDispatcherOwner {
 
@@ -64,6 +68,7 @@
 
     // Lazily recreated from NonConfigurationInstances by getViewModelStore()
     private ViewModelStore mViewModelStore;
+    private ViewModelProvider.Factory mDefaultFactory;
 
     private final OnBackPressedDispatcher mOnBackPressedDispatcher =
             new OnBackPressedDispatcher(new Runnable() {
@@ -273,6 +278,29 @@
     }
 
     /**
+     * {@inheritDoc}
+     *
+     * <p>The extras of {@link #getIntent()} when this is first called will be used as
+     * the defaults to any {@link androidx.lifecycle.SavedStateHandle} passed to a view model
+     * created using this factory.</p>
+     */
+    @NonNull
+    @Override
+    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
+        if (getApplication() == null) {
+            throw new IllegalStateException("Your activity is not yet attached to the "
+                    + "Application instance. You can't request ViewModel before onCreate call.");
+        }
+        if (mDefaultFactory == null) {
+            mDefaultFactory = new SavedStateViewModelFactory(
+                    getApplication(),
+                    this,
+                    getIntent() != null ? getIntent().getExtras() : null);
+        }
+        return mDefaultFactory;
+    }
+
+    /**
      * Called when the activity has detected the user's press of the back
      * key. The {@link #getOnBackPressedDispatcher() OnBackPressedDispatcher} will be given a
      * chance to handle the back button before the default behavior of
diff --git a/ads/ads-identifier-common/api/1.0.0-alpha01.txt b/ads/ads-identifier-common/api/1.0.0-alpha01.txt
new file mode 100644
index 0000000..da4f6cc
--- /dev/null
+++ b/ads/ads-identifier-common/api/1.0.0-alpha01.txt
@@ -0,0 +1 @@
+// Signature format: 3.0
diff --git a/ads/ads-identifier-common/api/current.txt b/ads/ads-identifier-common/api/current.txt
new file mode 100644
index 0000000..da4f6cc
--- /dev/null
+++ b/ads/ads-identifier-common/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 3.0
diff --git a/benchmark/api/res-1.0.0-alpha01.txt b/ads/ads-identifier-common/api/res-1.0.0-alpha01.txt
similarity index 100%
copy from benchmark/api/res-1.0.0-alpha01.txt
copy to ads/ads-identifier-common/api/res-1.0.0-alpha01.txt
diff --git a/ads/ads-identifier-common/api/restricted_1.0.0-alpha01.txt b/ads/ads-identifier-common/api/restricted_1.0.0-alpha01.txt
new file mode 100644
index 0000000..ae9f628
--- /dev/null
+++ b/ads/ads-identifier-common/api/restricted_1.0.0-alpha01.txt
@@ -0,0 +1,11 @@
+// Signature format: 3.0
+package androidx.ads.identifier {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class AdvertisingIdUtils {
+    method public static java.util.List<android.content.pm.ResolveInfo!> getAdvertisingIdProviderServices(android.content.pm.PackageManager);
+    method public static android.content.pm.ServiceInfo? selectServiceByPriority(java.util.List<android.content.pm.ResolveInfo!>?, android.content.pm.PackageManager);
+    field public static final String GET_AD_ID_ACTION = "androidx.ads.identifier.provider.GET_AD_ID";
+  }
+
+}
+
diff --git a/ads/ads-identifier-common/api/restricted_current.txt b/ads/ads-identifier-common/api/restricted_current.txt
new file mode 100644
index 0000000..ae9f628
--- /dev/null
+++ b/ads/ads-identifier-common/api/restricted_current.txt
@@ -0,0 +1,11 @@
+// Signature format: 3.0
+package androidx.ads.identifier {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class AdvertisingIdUtils {
+    method public static java.util.List<android.content.pm.ResolveInfo!> getAdvertisingIdProviderServices(android.content.pm.PackageManager);
+    method public static android.content.pm.ServiceInfo? selectServiceByPriority(java.util.List<android.content.pm.ResolveInfo!>?, android.content.pm.PackageManager);
+    field public static final String GET_AD_ID_ACTION = "androidx.ads.identifier.provider.GET_AD_ID";
+  }
+
+}
+
diff --git a/benchmark/build.gradle b/ads/ads-identifier-common/build.gradle
similarity index 61%
copy from benchmark/build.gradle
copy to ads/ads-identifier-common/build.gradle
index 386f71d..3433fb7 100644
--- a/benchmark/build.gradle
+++ b/ads/ads-identifier-common/build.gradle
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -26,20 +26,26 @@
 }
 
 dependencies {
-    implementation(ANDROIDX_TEST_RULES)
-    implementation(ANDROIDX_TEST_RUNNER)
-    implementation(KOTLIN_STDLIB)
-    implementation(SUPPORT_ANNOTATIONS)
+    implementation("androidx.annotation:annotation:1.1.0")
 
-    androidTestImplementation(ANDROIDX_TEST_CORE)
-    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    testImplementation(ANDROIDX_TEST_CORE)
+    testImplementation(ANDROIDX_TEST_RUNNER)
+    testImplementation(JUNIT)
+    testImplementation(TRUTH)
+    testImplementation(MOCKITO_CORE)
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 14
+    }
 }
 
 androidx {
-    name = "Android Benchmark"
+    name = "AndroidX Ads Identifier Common"
     publish = Publish.SNAPSHOT_AND_RELEASE
-    mavenVersion = LibraryVersions.BENCHMARK
-    mavenGroup = LibraryGroups.BENCHMARK
-    inceptionYear = "2018"
-    description = "Android Benchmark"
+    mavenVersion = LibraryVersions.ADS_IDENTIFIER
+    mavenGroup = LibraryGroups.ADS
+    inceptionYear = "2019"
+    description = "AndroidX Ads Identifier Common"
 }
diff --git a/ads/ads-identifier-common/src/main/AndroidManifest.xml b/ads/ads-identifier-common/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0d3cb05
--- /dev/null
+++ b/ads/ads-identifier-common/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+<manifest package="androidx.ads.identifier.common"
+    xmlns:android="http://schemas.android.com/apk/res/android">
+</manifest>
diff --git a/ads/ads-identifier-common/src/main/aidl/androidx/ads/identifier/provider/IAdvertisingIdService.aidl b/ads/ads-identifier-common/src/main/aidl/androidx/ads/identifier/provider/IAdvertisingIdService.aidl
new file mode 100644
index 0000000..c5bdc98
--- /dev/null
+++ b/ads/ads-identifier-common/src/main/aidl/androidx/ads/identifier/provider/IAdvertisingIdService.aidl
@@ -0,0 +1,13 @@
+package androidx.ads.identifier.provider;
+
+/**
+ * The Advertising ID service used to communicate between an Advertising ID Provider and the
+ * developer library.
+ *
+ * <p>The Advertising ID is a resettable identifier used for ads purpose.
+ * @hide
+ */
+interface IAdvertisingIdService {
+    String getId() = 0;
+    boolean isLimitAdTrackingEnabled() = 1;
+}
diff --git a/ads/ads-identifier-common/src/main/java/androidx/ads/identifier/AdvertisingIdUtils.java b/ads/ads-identifier-common/src/main/java/androidx/ads/identifier/AdvertisingIdUtils.java
new file mode 100644
index 0000000..3312079
--- /dev/null
+++ b/ads/ads-identifier-common/src/main/java/androidx/ads/identifier/AdvertisingIdUtils.java
@@ -0,0 +1,173 @@
+/*
+ * 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.ads.identifier;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Internal utilities for Advertising ID.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class AdvertisingIdUtils {
+
+    /**
+     * The Intent action used to identify an Advertising ID Provider. The Advertising ID Provider
+     * Service should declare this as an intent-filter, so that clients can find it.
+     */
+    public static final String GET_AD_ID_ACTION = "androidx.ads.identifier.provider.GET_AD_ID";
+
+    /**
+     * The permission used to indicate which Advertising ID Provider should be used in case there
+     * are multiple Advertising ID Providers on the device. Device manufacturer (OEM) should only
+     * grant this permission to the designated Advertising ID Provider.
+     */
+    @VisibleForTesting
+    static final String HIGH_PRIORITY_PERMISSION = "androidx.ads.identifier.provider.HIGH_PRIORITY";
+
+    AdvertisingIdUtils() {
+    }
+
+    /**
+     * Retrieves a list of all Advertising ID Providers' services on this device.
+     *
+     * <p>This is achieved by looking up which services can handle {@link #GET_AD_ID_ACTION}
+     * intent action.
+     * <p>Only system-level providers will be returned.
+     */
+    @NonNull
+    public static List<ResolveInfo> getAdvertisingIdProviderServices(
+            @NonNull PackageManager packageManager) {
+        Intent intent = new Intent(GET_AD_ID_ACTION);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            List<ResolveInfo> resolveInfos =
+                    packageManager.queryIntentServices(intent, PackageManager.MATCH_SYSTEM_ONLY);
+            return resolveInfos != null ? resolveInfos : Collections.emptyList();
+        }
+
+        List<ResolveInfo> resolveInfos = packageManager.queryIntentServices(intent, 0);
+        if (resolveInfos == null || resolveInfos.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<ResolveInfo> systemLevelResolveInfos = new ArrayList<>();
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+            ApplicationInfo applicationInfo;
+            try {
+                applicationInfo = packageManager.getApplicationInfo(serviceInfo.packageName, 0);
+            } catch (PackageManager.NameNotFoundException ignored) {
+                // Ignore this provider if name not found.
+                continue;
+            }
+            if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                systemLevelResolveInfos.add(resolveInfo);
+            }
+        }
+        return systemLevelResolveInfos;
+    }
+
+    /**
+     * Selects the Service of an Advertising ID Provider which should be used by developer
+     * library when requesting an Advertising ID.
+     *
+     * <p>Note: This method should only be used with the {@link ResolveInfo}s from
+     * {@link #getAdvertisingIdProviderServices} method, this currently means that only
+     * system-level Providers will be selected.
+     * <p>It will return the same Advertising ID Provider for all apps which use the developer
+     * library, using this priority:
+     * <ol>
+     * <li>Providers with {@link #HIGH_PRIORITY_PERMISSION} permission
+     * <li>Other Providers
+     * </ol>
+     * <p>If there are ties in any of the above categories, it will use this priority:
+     * <ol>
+     * <li>First app by earliest install time ({@link PackageInfo#firstInstallTime})
+     * <li>First app by package name alphabetically sorted
+     * </ol>
+     *
+     * @return null if the input {@code resolveInfos} is null or empty, or non of the input
+     * package is found.
+     */
+    @Nullable
+    public static ServiceInfo selectServiceByPriority(
+            @Nullable List<ResolveInfo> resolveInfos, @NonNull PackageManager packageManager) {
+        if (resolveInfos == null || resolveInfos.isEmpty()) {
+            return null;
+        }
+        ServiceInfo selectedServiceInfo = null;
+        PackageInfo selectedPackageInfo = null;
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+            PackageInfo packageInfo;
+            try {
+                packageInfo =
+                        packageManager.getPackageInfo(
+                                serviceInfo.packageName, PackageManager.GET_PERMISSIONS);
+            } catch (PackageManager.NameNotFoundException ignored) {
+                // Ignore this provider if name not found.
+                continue;
+            }
+            if (selectedPackageInfo == null
+                    || hasHigherPriority(packageInfo, selectedPackageInfo)) {
+                selectedServiceInfo = serviceInfo;
+                selectedPackageInfo = packageInfo;
+            }
+        }
+        return selectedServiceInfo;
+    }
+
+    private static boolean hasHigherPriority(PackageInfo candidate, PackageInfo currentHighest) {
+        boolean isCandidateRequestHighPriority = isRequestHighPriority(candidate);
+        boolean isCurrentHighestRequestHighPriority = isRequestHighPriority(currentHighest);
+        if (isCandidateRequestHighPriority != isCurrentHighestRequestHighPriority) {
+            return isCandidateRequestHighPriority;
+        }
+        if (candidate.firstInstallTime != currentHighest.firstInstallTime) {
+            return candidate.firstInstallTime < currentHighest.firstInstallTime;
+        }
+        return candidate.packageName.compareTo(currentHighest.packageName) < 0;
+    }
+
+    private static boolean isRequestHighPriority(PackageInfo packageInfo) {
+        if (packageInfo.requestedPermissions == null) {
+            return false;
+        }
+        for (String permission : packageInfo.requestedPermissions) {
+            if (HIGH_PRIORITY_PERMISSION.equals(permission)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/ads/ads-identifier-common/src/test/java/androidx/ads/identifier/AdvertisingIdUtilsTest.java b/ads/ads-identifier-common/src/test/java/androidx/ads/identifier/AdvertisingIdUtilsTest.java
new file mode 100644
index 0000000..221ee4e
--- /dev/null
+++ b/ads/ads-identifier-common/src/test/java/androidx/ads/identifier/AdvertisingIdUtilsTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.ads.identifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+
+import com.google.common.collect.Lists;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class AdvertisingIdUtilsTest {
+    @Test
+    public void selectServiceByPriority() throws Exception {
+        PackageManager packageManager = mock(PackageManager.class);
+
+        List<ResolveInfo> resolveInfos = Lists.newArrayList(
+                createPackageInfo("c.normal.1", false, 1, packageManager),
+                createPackageInfo("y.normal.0", false, 0, packageManager),
+                createPackageInfo("x.normal.0", false, 0, packageManager),
+                createPackageInfo("z.high.2", true, 2, packageManager));
+
+        List<String> priorityList = getPriorityList(resolveInfos, packageManager);
+
+        assertThat(priorityList).containsExactly(
+                "z.high.2",
+                "x.normal.0",
+                "y.normal.0",
+                "c.normal.1"
+        ).inOrder();
+    }
+
+    @Test
+    public void selectServiceByPriority_firstInstallTime() throws Exception {
+        PackageManager packageManager = mock(PackageManager.class);
+
+        List<ResolveInfo> resolveInfos = Lists.newArrayList(
+                createPackageInfo("com.a", false, 2, packageManager),
+                createPackageInfo("com.b", false, 9, packageManager),
+                createPackageInfo("com.c", false, 7, packageManager),
+                createPackageInfo("com.d", false, 10, packageManager),
+                createPackageInfo("com.e", false, 0, packageManager));
+
+        List<String> priorityList = getPriorityList(resolveInfos, packageManager);
+
+        assertThat(priorityList).containsExactly(
+                "com.e",
+                "com.a",
+                "com.c",
+                "com.b",
+                "com.d"
+        ).inOrder();
+    }
+
+    @Test
+    public void selectServiceByPriority_packageName() throws Exception {
+        PackageManager packageManager = mock(PackageManager.class);
+
+        List<ResolveInfo> resolveInfos = Lists.newArrayList(
+                createPackageInfo("com.abc.id", false, 0, packageManager),
+                createPackageInfo("com.abc", false, 0, packageManager),
+                createPackageInfo("org.example", false, 0, packageManager),
+                createPackageInfo("com.abcde", false, 0, packageManager),
+                createPackageInfo("com.abcde_id", false, 0, packageManager));
+
+        List<String> priorityList = getPriorityList(resolveInfos, packageManager);
+
+        assertThat(priorityList).containsExactly(
+                "com.abc",
+                "com.abc.id",
+                "com.abcde",
+                "com.abcde_id",
+                "org.example"
+        ).inOrder();
+    }
+
+    private List<String> getPriorityList(List<ResolveInfo> resolveInfos,
+            PackageManager packageManager) {
+        List<String> result = new ArrayList<>();
+        while (resolveInfos.size() > 0) {
+            final ServiceInfo serviceInfo =
+                    AdvertisingIdUtils.selectServiceByPriority(resolveInfos, packageManager);
+
+            result.add(serviceInfo.packageName);
+
+            resolveInfos.removeIf(resolveInfo -> resolveInfo.serviceInfo == serviceInfo);
+        }
+        return result;
+    }
+
+    @Test
+    public void selectServiceByPriority_inputNull() throws Exception {
+        PackageManager packageManager = mock(PackageManager.class);
+
+        ServiceInfo serviceInfo =
+                AdvertisingIdUtils.selectServiceByPriority(null, packageManager);
+
+        assertThat(serviceInfo).isNull();
+    }
+
+    @Test
+    public void selectServiceByPriority_inputEmpty() throws Exception {
+        PackageManager packageManager = mock(PackageManager.class);
+
+        ServiceInfo serviceInfo =
+                AdvertisingIdUtils.selectServiceByPriority(Collections.emptyList(), packageManager);
+
+        assertThat(serviceInfo).isNull();
+    }
+
+    private ResolveInfo createPackageInfo(String packageName,
+            boolean requestHighPriority, long firstInstallTime, PackageManager packageManager)
+            throws Exception {
+        PackageInfo packageInfo = mock(PackageInfo.class);
+        packageInfo.packageName = packageName;
+        if (requestHighPriority) {
+            packageInfo.requestedPermissions =
+                    new String[]{AdvertisingIdUtils.HIGH_PRIORITY_PERMISSION};
+        }
+        packageInfo.firstInstallTime = firstInstallTime;
+
+        mockGetPackageInfo(packageInfo, packageManager);
+
+        ResolveInfo resolveInfo = mock(ResolveInfo.class);
+        resolveInfo.serviceInfo = mock(ServiceInfo.class);
+        resolveInfo.serviceInfo.packageName = packageName;
+        return resolveInfo;
+    }
+
+    private void mockGetPackageInfo(PackageInfo packageInfo, PackageManager packageManager)
+            throws Exception {
+        when(packageManager.getPackageInfo(eq(packageInfo.packageName),
+                eq(PackageManager.GET_PERMISSIONS))).thenReturn(packageInfo);
+    }
+}
diff --git a/ads/ads-identifier-provider/api/1.0.0-alpha01.txt b/ads/ads-identifier-provider/api/1.0.0-alpha01.txt
new file mode 100644
index 0000000..463129f
--- /dev/null
+++ b/ads/ads-identifier-provider/api/1.0.0-alpha01.txt
@@ -0,0 +1,21 @@
+// Signature format: 3.0
+package androidx.ads.identifier.provider {
+
+  public interface AdvertisingIdProvider {
+    method public String getId();
+    method public boolean isLimitAdTrackingEnabled();
+  }
+
+  public abstract class AdvertisingIdProviderInfo {
+    method public abstract String getPackageName();
+    method public abstract android.content.Intent? getSettingsIntent();
+    method public abstract boolean isHighestPriority();
+  }
+
+  public class AdvertisingIdProviderManager {
+    method public static java.util.List<androidx.ads.identifier.provider.AdvertisingIdProviderInfo!> getAdvertisingIdProviders(android.content.Context);
+    method public static void registerProviderCallable(java.util.concurrent.Callable<androidx.ads.identifier.provider.AdvertisingIdProvider!>);
+  }
+
+}
+
diff --git a/ads/ads-identifier-provider/api/current.txt b/ads/ads-identifier-provider/api/current.txt
new file mode 100644
index 0000000..463129f
--- /dev/null
+++ b/ads/ads-identifier-provider/api/current.txt
@@ -0,0 +1,21 @@
+// Signature format: 3.0
+package androidx.ads.identifier.provider {
+
+  public interface AdvertisingIdProvider {
+    method public String getId();
+    method public boolean isLimitAdTrackingEnabled();
+  }
+
+  public abstract class AdvertisingIdProviderInfo {
+    method public abstract String getPackageName();
+    method public abstract android.content.Intent? getSettingsIntent();
+    method public abstract boolean isHighestPriority();
+  }
+
+  public class AdvertisingIdProviderManager {
+    method public static java.util.List<androidx.ads.identifier.provider.AdvertisingIdProviderInfo!> getAdvertisingIdProviders(android.content.Context);
+    method public static void registerProviderCallable(java.util.concurrent.Callable<androidx.ads.identifier.provider.AdvertisingIdProvider!>);
+  }
+
+}
+
diff --git a/benchmark/api/res-1.0.0-alpha01.txt b/ads/ads-identifier-provider/api/res-1.0.0-alpha01.txt
similarity index 100%
copy from benchmark/api/res-1.0.0-alpha01.txt
copy to ads/ads-identifier-provider/api/res-1.0.0-alpha01.txt
diff --git a/ads/ads-identifier-provider/api/restricted_1.0.0-alpha01.txt b/ads/ads-identifier-provider/api/restricted_1.0.0-alpha01.txt
new file mode 100644
index 0000000..463129f
--- /dev/null
+++ b/ads/ads-identifier-provider/api/restricted_1.0.0-alpha01.txt
@@ -0,0 +1,21 @@
+// Signature format: 3.0
+package androidx.ads.identifier.provider {
+
+  public interface AdvertisingIdProvider {
+    method public String getId();
+    method public boolean isLimitAdTrackingEnabled();
+  }
+
+  public abstract class AdvertisingIdProviderInfo {
+    method public abstract String getPackageName();
+    method public abstract android.content.Intent? getSettingsIntent();
+    method public abstract boolean isHighestPriority();
+  }
+
+  public class AdvertisingIdProviderManager {
+    method public static java.util.List<androidx.ads.identifier.provider.AdvertisingIdProviderInfo!> getAdvertisingIdProviders(android.content.Context);
+    method public static void registerProviderCallable(java.util.concurrent.Callable<androidx.ads.identifier.provider.AdvertisingIdProvider!>);
+  }
+
+}
+
diff --git a/ads/ads-identifier-provider/api/restricted_current.txt b/ads/ads-identifier-provider/api/restricted_current.txt
new file mode 100644
index 0000000..463129f
--- /dev/null
+++ b/ads/ads-identifier-provider/api/restricted_current.txt
@@ -0,0 +1,21 @@
+// Signature format: 3.0
+package androidx.ads.identifier.provider {
+
+  public interface AdvertisingIdProvider {
+    method public String getId();
+    method public boolean isLimitAdTrackingEnabled();
+  }
+
+  public abstract class AdvertisingIdProviderInfo {
+    method public abstract String getPackageName();
+    method public abstract android.content.Intent? getSettingsIntent();
+    method public abstract boolean isHighestPriority();
+  }
+
+  public class AdvertisingIdProviderManager {
+    method public static java.util.List<androidx.ads.identifier.provider.AdvertisingIdProviderInfo!> getAdvertisingIdProviders(android.content.Context);
+    method public static void registerProviderCallable(java.util.concurrent.Callable<androidx.ads.identifier.provider.AdvertisingIdProvider!>);
+  }
+
+}
+
diff --git a/ads/ads-identifier-provider/build.gradle b/ads/ads-identifier-provider/build.gradle
new file mode 100644
index 0000000..535bbde
--- /dev/null
+++ b/ads/ads-identifier-provider/build.gradle
@@ -0,0 +1,59 @@
+/*
+ * 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 static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("kotlin-android")
+}
+
+dependencies {
+    implementation("androidx.annotation:annotation:1.1.0")
+    implementation("androidx.core:core:1.1.0")
+    implementation(AUTO_VALUE_ANNOTATIONS)
+    annotationProcessor(AUTO_VALUE)
+
+    implementation(project(":ads-identifier-common"))
+
+    androidTestImplementation(JUNIT)
+    androidTestImplementation(TRUTH)
+    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    androidTestImplementation(ANDROIDX_TEST_CORE)
+    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    androidTestImplementation(ANDROIDX_TEST_RULES)
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 14
+    }
+}
+
+androidx {
+    name = "AndroidX Ads Identifier Provider"
+    publish = Publish.SNAPSHOT_AND_RELEASE
+    mavenVersion = LibraryVersions.ADS_IDENTIFIER
+    mavenGroup = LibraryGroups.ADS
+    inceptionYear = "2019"
+    description = "AndroidX Ads Identifier Provider"
+}
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/build.gradle b/ads/ads-identifier-provider/integration-tests/testapp/build.gradle
new file mode 100644
index 0000000..bd185ba
--- /dev/null
+++ b/ads/ads-identifier-provider/integration-tests/testapp/build.gradle
@@ -0,0 +1,34 @@
+/*
+ * 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 static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.application")
+}
+
+android {
+    defaultConfig {
+        applicationId "androidx.ads.identifier.provider.testapp"
+        minSdkVersion 14
+    }
+}
+
+dependencies {
+    implementation(project(":ads-identifier-provider"))
+    implementation("androidx.annotation:annotation:1.1.0")
+}
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/src/main/AndroidManifest.xml b/ads/ads-identifier-provider/integration-tests/testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..259681e
--- /dev/null
+++ b/ads/ads-identifier-provider/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="androidx.ads.identifier.provider.testapp">
+
+    <application
+        android:name=".AdsIdentifierProviderApplication"
+        android:allowBackup="false"
+        android:label="@string/app_name"
+        tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon">
+
+        <activity
+            android:name=".AdsIdentifierProviderActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderActivity.java b/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderActivity.java
new file mode 100644
index 0000000..9de822e
--- /dev/null
+++ b/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderActivity.java
@@ -0,0 +1,55 @@
+/*
+ * 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.ads.identifier.provider.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.ads.identifier.provider.AdvertisingIdProviderInfo;
+import androidx.ads.identifier.provider.AdvertisingIdProviderManager;
+
+import java.util.List;
+
+/**
+ * Simple activity as an Advertising ID Provider.
+ */
+public class AdsIdentifierProviderActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_ads_identifier_provider);
+    }
+
+    /** Lists all the Advertising ID Providers. */
+    public void listProviders(View view) {
+        TextView textView = findViewById(R.id.text);
+        textView.setText("All providers:\n");
+
+        List<AdvertisingIdProviderInfo> allAdIdProviders =
+                AdvertisingIdProviderManager.getAdvertisingIdProviders(this);
+
+        for (AdvertisingIdProviderInfo providerInfo : allAdIdProviders) {
+            textView.append("Package name: " + providerInfo.getPackageName() + "\n");
+            textView.append("Settings UI intent: " + providerInfo.getSettingsIntent() + "\n");
+            textView.append("Is highest priority: " + providerInfo.isHighestPriority() + "\n");
+            textView.append("\n");
+        }
+    }
+}
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderApplication.java b/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderApplication.java
new file mode 100644
index 0000000..77e2fea
--- /dev/null
+++ b/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/AdsIdentifierProviderApplication.java
@@ -0,0 +1,35 @@
+/*
+ * 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.ads.identifier.provider.testapp;
+
+import android.app.Application;
+
+import androidx.ads.identifier.provider.AdvertisingIdProviderManager;
+
+/**
+ * This will show you how to make your own Advertising ID Provider for providing the developer
+ * library an Advertising ID when requested by apps.
+ */
+public class AdsIdentifierProviderApplication extends Application {
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        AdvertisingIdProviderManager.registerProviderCallable(SampleAdvertisingIdProvider::new);
+    }
+}
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/SampleAdvertisingIdProvider.java b/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/SampleAdvertisingIdProvider.java
new file mode 100644
index 0000000..fc5c2af
--- /dev/null
+++ b/ads/ads-identifier-provider/integration-tests/testapp/src/main/java/androidx/ads/identifier/provider/testapp/SampleAdvertisingIdProvider.java
@@ -0,0 +1,35 @@
+/*
+ * 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.ads.identifier.provider.testapp;
+
+import androidx.ads.identifier.provider.AdvertisingIdProvider;
+import androidx.annotation.NonNull;
+
+/** An example Advertising ID Provider which always returns same ID. */
+public class SampleAdvertisingIdProvider implements AdvertisingIdProvider {
+
+    @NonNull
+    @Override
+    public String getId() {
+        return "308f629d-c857-4026-8b62-7bdd71caaaaa";
+    }
+
+    @Override
+    public boolean isLimitAdTrackingEnabled() {
+        return false;
+    }
+}
diff --git a/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/layout/activity_ads_identifier_provider.xml b/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/layout/activity_ads_identifier_provider.xml
new file mode 100644
index 0000000..8b76e30
--- /dev/null
+++ b/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/layout/activity_ads_identifier_provider.xml
@@ -0,0 +1,46 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    tools:context="androidx.ads.identifier.provider.testapp.AdsIdentifierProviderActivity">
+
+    <LinearLayout
+        style="?android:attr/buttonBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:onClick="listProviders"
+            android:text="@string/list_providers" />
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="" />
+</LinearLayout>
diff --git a/benchmark/src/androidTest/AndroidManifest.xml b/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/values/strings.xml
similarity index 65%
copy from benchmark/src/androidTest/AndroidManifest.xml
copy to ads/ads-identifier-provider/integration-tests/testapp/src/main/res/values/strings.xml
index f5ec776..57ce496 100644
--- a/benchmark/src/androidTest/AndroidManifest.xml
+++ b/ads/ads-identifier-provider/integration-tests/testapp/src/main/res/values/strings.xml
@@ -14,13 +14,8 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<manifest
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="androidx.benchmark.test">
 
-    <application
-        android:name="androidx.benchmark.ArgumentInjectingApplication">
-        <activity android:name="android.app.Activity"/>
-    </application>
-</manifest>
\ No newline at end of file
+<resources>
+    <string name="app_name">Ad ID Provider</string>
+    <string name="list_providers">List Providers</string>
+</resources>
\ No newline at end of file
diff --git a/ads/ads-identifier-provider/src/androidTest/AndroidManifest.xml b/ads/ads-identifier-provider/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..237644a
--- /dev/null
+++ b/ads/ads-identifier-provider/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="androidx.ads.identifier.provider.test">
+</manifest>
diff --git a/ads/ads-identifier-provider/src/androidTest/java/androidx/ads/identifier/provider/AdvertisingIdProviderManagerTest.java b/ads/ads-identifier-provider/src/androidTest/java/androidx/ads/identifier/provider/AdvertisingIdProviderManagerTest.java
new file mode 100644
index 0000000..a1e7f9f6
--- /dev/null
+++ b/ads/ads-identifier-provider/src/androidTest/java/androidx/ads/identifier/provider/AdvertisingIdProviderManagerTest.java
@@ -0,0 +1,284 @@
+/*
+ * 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.ads.identifier.provider;
+
+import static androidx.ads.identifier.AdvertisingIdUtils.GET_AD_ID_ACTION;
+import static androidx.ads.identifier.provider.AdvertisingIdProviderManager.OPEN_SETTINGS_ACTION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Build;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.google.common.collect.Lists;
+import com.google.common.truth.Correspondence;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AdvertisingIdProviderManagerTest {
+
+    private static final Correspondence<AdvertisingIdProviderInfo, AdvertisingIdProviderInfo>
+            PROVIDER_INFO_EQUALITY = Correspondence.from(
+            AdvertisingIdProviderManagerTest::isProviderInfoEqual, "is equivalent to");
+
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock
+    private PackageManager mMockPackageManager;
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+        mContext = new ContextWrapper(context) {
+            @Override
+            public PackageManager getPackageManager() {
+                return mMockPackageManager;
+            }
+        };
+    }
+
+    private ResolveInfo createServiceResolveInfo(String packageName) {
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.serviceInfo = new ServiceInfo();
+        resolveInfo.serviceInfo.packageName = packageName;
+        return resolveInfo;
+    }
+
+    private ResolveInfo createActivityResolveInfo(String packageName, String name) {
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.packageName = packageName;
+        resolveInfo.activityInfo.name = name;
+        return resolveInfo;
+    }
+
+    private void mockQueryIntentServices(List<ResolveInfo> resolveInfos) throws Exception {
+        boolean supportMatchSystemOnly = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
+        int flags = supportMatchSystemOnly ? PackageManager.MATCH_SYSTEM_ONLY : 0;
+        when(mMockPackageManager.queryIntentServices(
+                argThat(intent -> intent != null && GET_AD_ID_ACTION.equals(intent.getAction())),
+                eq(flags))).thenReturn(resolveInfos);
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            if (!supportMatchSystemOnly) {
+                ApplicationInfo applicationInfo = new ApplicationInfo();
+                applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+                when(mMockPackageManager.getApplicationInfo(resolveInfo.serviceInfo.packageName, 0))
+                        .thenReturn(applicationInfo);
+            }
+            PackageInfo packageInfo = new PackageInfo();
+            packageInfo.packageName = resolveInfo.serviceInfo.packageName;
+            when(mMockPackageManager.getPackageInfo(packageInfo.packageName,
+                    PackageManager.GET_PERMISSIONS)).thenReturn(packageInfo);
+        }
+    }
+
+    private void mockQueryIntentActivities(List<ResolveInfo> resolveInfos) {
+        when(mMockPackageManager.queryIntentActivities(
+                argThat(intent -> intent != null
+                        && OPEN_SETTINGS_ACTION.equals(intent.getAction())),
+                eq(0))).thenReturn(resolveInfos);
+    }
+
+    @Test
+    public void getAllAdIdProviders_onlySelf() throws Exception {
+        mockQueryIntentServices(
+                Lists.newArrayList(createServiceResolveInfo(mContext.getPackageName())));
+
+        assertThat(AdvertisingIdProviderManager.getAdvertisingIdProviders(mContext))
+                .comparingElementsUsing(PROVIDER_INFO_EQUALITY)
+                .containsExactly(
+                        AdvertisingIdProviderInfo.builder()
+                                .setPackageName(mContext.getPackageName())
+                                .setHighestPriority(true)
+                                .build());
+    }
+
+    @Test
+    public void getAllAdIdProviders_noProvider() {
+        assertThat(AdvertisingIdProviderManager.getAdvertisingIdProviders(mContext)).isEmpty();
+    }
+
+    @Test
+    public void getAllAdIdProviders() throws Exception {
+        mockQueryIntentServices(
+                Lists.newArrayList(
+                        createServiceResolveInfo(mContext.getPackageName()),
+                        createServiceResolveInfo("com.a")));
+
+        assertThat(AdvertisingIdProviderManager.getAdvertisingIdProviders(mContext))
+                .comparingElementsUsing(PROVIDER_INFO_EQUALITY)
+                .containsExactly(
+                        AdvertisingIdProviderInfo.builder()
+                                .setPackageName(mContext.getPackageName())
+                                .setHighestPriority(true)
+                                .build(),
+                        AdvertisingIdProviderInfo.builder().setPackageName("com.a").build());
+    }
+
+    @Test
+    public void getAllAdIdProviders_withOpenIntent() throws Exception {
+        mockQueryIntentServices(
+                Lists.newArrayList(
+                        createServiceResolveInfo(mContext.getPackageName()),
+                        createServiceResolveInfo("com.a")));
+
+        mockQueryIntentActivities(
+                Lists.newArrayList(
+                        createActivityResolveInfo(mContext.getPackageName(), "Activity"),
+                        createActivityResolveInfo("com.a", "A")));
+
+        assertThat(AdvertisingIdProviderManager.getAdvertisingIdProviders(mContext))
+                .comparingElementsUsing(PROVIDER_INFO_EQUALITY)
+                .containsExactly(
+                        AdvertisingIdProviderInfo.builder()
+                                .setPackageName(mContext.getPackageName())
+                                .setSettingsIntent(new Intent(OPEN_SETTINGS_ACTION)
+                                        .setClassName(mContext.getPackageName(), "Activity"))
+                                .setHighestPriority(true)
+                                .build(),
+                        AdvertisingIdProviderInfo.builder()
+                                .setPackageName("com.a")
+                                .setSettingsIntent(new Intent(OPEN_SETTINGS_ACTION)
+                                        .setClassName("com.a", "A"))
+                                .build());
+    }
+
+    @Test
+    public void getAllAdIdProviders_twoOtherProviders() throws Exception {
+        mockQueryIntentServices(
+                Lists.newArrayList(
+                        createServiceResolveInfo(mContext.getPackageName()),
+                        createServiceResolveInfo("com.a"),
+                        createServiceResolveInfo("com.b")));
+
+        mockQueryIntentActivities(
+                Lists.newArrayList(
+                        createActivityResolveInfo(mContext.getPackageName(), "Activity"),
+                        createActivityResolveInfo("com.a", "A")));
+
+        assertThat(AdvertisingIdProviderManager.getAdvertisingIdProviders(mContext))
+                .comparingElementsUsing(PROVIDER_INFO_EQUALITY)
+                .containsExactly(
+                        AdvertisingIdProviderInfo.builder()
+                                .setPackageName(mContext.getPackageName())
+                                .setSettingsIntent(new Intent(OPEN_SETTINGS_ACTION)
+                                        .setClassName(mContext.getPackageName(), "Activity"))
+                                .setHighestPriority(true)
+                                .build(),
+                        AdvertisingIdProviderInfo.builder()
+                                .setPackageName("com.a")
+                                .setSettingsIntent(new Intent(OPEN_SETTINGS_ACTION)
+                                        .setClassName("com.a", "A"))
+                                .build(),
+                        AdvertisingIdProviderInfo.builder()
+                                .setPackageName("com.b")
+                                .build());
+    }
+
+    @Test
+    public void getAllAdIdProviders_extraOpenIntent() throws Exception {
+        mockQueryIntentServices(
+                Lists.newArrayList(
+                        createServiceResolveInfo(mContext.getPackageName()),
+                        createServiceResolveInfo("com.a")));
+
+        mockQueryIntentActivities(
+                Lists.newArrayList(
+                        createActivityResolveInfo(mContext.getPackageName(), "Activity"),
+                        createActivityResolveInfo("com.a", "A"),
+                        createActivityResolveInfo("com.b", "B")));
+
+        assertThat(AdvertisingIdProviderManager.getAdvertisingIdProviders(mContext))
+                .comparingElementsUsing(PROVIDER_INFO_EQUALITY)
+                .containsExactly(
+                        AdvertisingIdProviderInfo.builder()
+                                .setPackageName(mContext.getPackageName())
+                                .setSettingsIntent(new Intent(OPEN_SETTINGS_ACTION)
+                                        .setClassName(mContext.getPackageName(), "Activity"))
+                                .setHighestPriority(true)
+                                .build(),
+                        AdvertisingIdProviderInfo.builder()
+                                .setPackageName("com.a")
+                                .setSettingsIntent(new Intent(OPEN_SETTINGS_ACTION)
+                                        .setClassName("com.a", "A"))
+                                .build());
+    }
+
+    @Test
+    public void registerProviderCallable() {
+        Callable<AdvertisingIdProvider> providerCallable = () -> null;
+
+        AdvertisingIdProviderManager.registerProviderCallable(providerCallable);
+
+        assertThat(AdvertisingIdProviderManager.getProviderCallable())
+                .isSameInstanceAs(providerCallable);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void registerProviderCallable_null() {
+        AdvertisingIdProviderManager.registerProviderCallable(null);
+    }
+
+    @Test
+    public void clearProviderCallable() {
+        Callable<AdvertisingIdProvider> providerCallable = () -> null;
+
+        AdvertisingIdProviderManager.registerProviderCallable(providerCallable);
+        AdvertisingIdProviderManager.clearProviderCallable();
+
+        assertThat(AdvertisingIdProviderManager.getProviderCallable()).isNull();
+    }
+
+    private static boolean isProviderInfoEqual(
+            AdvertisingIdProviderInfo actual, AdvertisingIdProviderInfo expected) {
+        return actual.getPackageName().equals(expected.getPackageName())
+                && (actual.getSettingsIntent() == null ? expected.getSettingsIntent() == null
+                : actual.getSettingsIntent().filterEquals(expected.getSettingsIntent()))
+                && actual.isHighestPriority() == expected.isHighestPriority();
+    }
+}
diff --git a/ads/ads-identifier-provider/src/androidTest/java/androidx/ads/identifier/provider/internal/AdvertisingIdServiceTest.java b/ads/ads-identifier-provider/src/androidTest/java/androidx/ads/identifier/provider/internal/AdvertisingIdServiceTest.java
new file mode 100644
index 0000000..edacc3e
--- /dev/null
+++ b/ads/ads-identifier-provider/src/androidTest/java/androidx/ads/identifier/provider/internal/AdvertisingIdServiceTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.ads.identifier.provider.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+
+import androidx.ads.identifier.AdvertisingIdUtils;
+import androidx.ads.identifier.provider.AdvertisingIdProvider;
+import androidx.ads.identifier.provider.AdvertisingIdProviderManager;
+import androidx.ads.identifier.provider.IAdvertisingIdService;
+import androidx.annotation.NonNull;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AdvertisingIdServiceTest {
+    private static final String TESTING_AD_ID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
+
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    private Context mContext;
+    private Intent mIntent;
+    private ServiceConnection mServiceConnection;
+
+    @Before
+    public void setUp() {
+        AdvertisingIdProviderManager.clearProviderCallable();
+
+        mContext = ApplicationProvider.getApplicationContext();
+
+        mIntent = new Intent(AdvertisingIdUtils.GET_AD_ID_ACTION);
+        mIntent.setClassName(mContext.getPackageName(), AdvertisingIdService.class.getName());
+    }
+
+    @After
+    public void tearDown() {
+        if (mServiceConnection != null) {
+            mContext.unbindService(mServiceConnection);
+        }
+        mContext.stopService(mIntent);
+    }
+
+    private IAdvertisingIdService getService() throws InterruptedException {
+        BlockingDeque<IAdvertisingIdService> blockingDeque = new LinkedBlockingDeque<>();
+        mServiceConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+                blockingDeque.add(IAdvertisingIdService.Stub.asInterface(iBinder));
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName componentName) {
+            }
+        };
+        mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+        return blockingDeque.poll(1, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void getId() throws Exception {
+        AdvertisingIdProviderManager.registerProviderCallable(
+                () -> new MockAdvertisingIdProvider(TESTING_AD_ID, true));
+
+        IAdvertisingIdService service = getService();
+
+        assertThat(service.getId()).isEqualTo(TESTING_AD_ID);
+        assertThat(service.isLimitAdTrackingEnabled()).isEqualTo(true);
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void getId_providerThrowsException() throws Exception {
+        AdvertisingIdProviderManager.registerProviderCallable(() -> {
+            MockAdvertisingIdProvider mockAdvertisingIdProvider =
+                    new MockAdvertisingIdProvider(TESTING_AD_ID, true);
+            mockAdvertisingIdProvider.mGetIdThrowsException = true;
+            return mockAdvertisingIdProvider;
+        });
+
+        IAdvertisingIdService service = getService();
+        service.getId();
+    }
+
+    private static class MockAdvertisingIdProvider implements AdvertisingIdProvider {
+        private final String mId;
+        private final boolean mLimitAdTrackingEnabled;
+        boolean mGetIdThrowsException = false;
+
+        MockAdvertisingIdProvider(String id, boolean limitAdTrackingEnabled) {
+            mId = id;
+            mLimitAdTrackingEnabled = limitAdTrackingEnabled;
+        }
+
+        @NonNull
+        @Override
+        public String getId() {
+            if (mGetIdThrowsException) {
+                throw new RuntimeException();
+            }
+            return mId;
+        }
+
+        @Override
+        public boolean isLimitAdTrackingEnabled() {
+            return mLimitAdTrackingEnabled;
+        }
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void getId_providerNotRegistered() {
+        AdvertisingIdService.getAdvertisingIdProvider();
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void getId_providerCallableThrowsException() {
+        AdvertisingIdProviderManager.registerProviderCallable(() -> {
+            throw new Exception();
+        });
+
+        AdvertisingIdService.getAdvertisingIdProvider();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void getId_providerCallableReturnsNull() {
+        AdvertisingIdProviderManager.registerProviderCallable(() -> null);
+
+        AdvertisingIdService.getAdvertisingIdProvider();
+    }
+}
diff --git a/ads/ads-identifier-provider/src/main/AndroidManifest.xml b/ads/ads-identifier-provider/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3a7221b4
--- /dev/null
+++ b/ads/ads-identifier-provider/src/main/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="androidx.ads.identifier.provider">
+
+    <application>
+        <service
+            android:name=".internal.AdvertisingIdService"
+            android:enabled="true"
+            android:exported="true"
+            android:visibleToInstantApps="true"
+            tools:ignore="ExportedService">
+            <intent-filter>
+                <action android:name="androidx.ads.identifier.provider.GET_AD_ID" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data
+                android:name="instantapps.clients.allowed"
+                android:value="true" />
+        </service>
+    </application>
+</manifest>
diff --git a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProvider.java b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProvider.java
new file mode 100644
index 0000000..682e90a
--- /dev/null
+++ b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProvider.java
@@ -0,0 +1,39 @@
+/*
+ * 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.ads.identifier.provider;
+
+import androidx.annotation.NonNull;
+
+/**
+ * The class for the AndroidX Advertising ID Provider that should provide the resettable ID and
+ * LAT preference should implement this interface.
+ *
+ * See {@link AdvertisingIdProviderManager} for more details.
+ *
+ * <p>Note: The implementation of this interface must be completely thread-safe.
+ */
+public interface AdvertisingIdProvider {
+    /**
+     * Retrieves the Advertising ID.
+     * <p>This ID will be normalized to UUID format by the developer library if it isn't already.
+     */
+    @NonNull
+    String getId();
+
+    /** Retrieves whether the user has chosen to limit ad tracking (ads personalization). */
+    boolean isLimitAdTrackingEnabled();
+}
diff --git a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderInfo.java b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderInfo.java
new file mode 100644
index 0000000..7a18345
--- /dev/null
+++ b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderInfo.java
@@ -0,0 +1,81 @@
+/*
+ * 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.ads.identifier.provider;
+
+import android.content.Intent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.auto.value.AutoValue;
+
+/**
+ * A {@link AdvertisingIdProviderInfo} represents the information about an Advertising ID Provider
+ * installed on the device.
+ *
+ * <p>Used in cases when there are multiple Advertising ID Providers on the device. See
+ * {@link AdvertisingIdProviderManager#getAdvertisingIdProviders} for more details.
+ */
+@AutoValue
+public abstract class AdvertisingIdProviderInfo {
+
+    // Create a no-args constructor so it doesn't appear in current.txt
+    AdvertisingIdProviderInfo() {
+    }
+
+    /** Retrieves the Advertising ID Provider package name. */
+    @NonNull
+    public abstract String getPackageName();
+
+    /**
+     * Retrieves the {@link Intent} to open the Advertising ID settings page for a given
+     * Advertising ID Provider.
+     *
+     * <p>This page should allow the user to reset Advertising IDs and change Limit Advertising
+     * Tracking preference.
+     */
+    @Nullable
+    public abstract Intent getSettingsIntent();
+
+    /**
+     * Retrieves whether the provider has the highest priority among all the providers for the
+     * developer library, meaning its provided ID will be used.
+     */
+    public abstract boolean isHighestPriority();
+
+    /** Create a {@link Builder}. */
+    static Builder builder() {
+        return new AutoValue_AdvertisingIdProviderInfo.Builder().setHighestPriority(false);
+    }
+
+    /** The builder for {@link AdvertisingIdProviderInfo}. */
+    @AutoValue.Builder
+    abstract static class Builder {
+
+        // Create a no-args constructor so it doesn't appear in current.txt
+        Builder() {
+        }
+
+        abstract Builder setPackageName(String packageName);
+
+        abstract Builder setSettingsIntent(Intent settingsIntent);
+
+        abstract Builder setHighestPriority(boolean highestPriority);
+
+        abstract AdvertisingIdProviderInfo build();
+    }
+}
diff --git a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderManager.java b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderManager.java
new file mode 100644
index 0000000..5899f40
--- /dev/null
+++ b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/AdvertisingIdProviderManager.java
@@ -0,0 +1,160 @@
+/*
+ * 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.ads.identifier.provider;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+
+import androidx.ads.identifier.AdvertisingIdUtils;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * The AdvertisingIdProviderManager will be used by an Advertising ID Provider to register the
+ * provider implementation or retrieve all the Advertising ID Providers on the device.
+ *
+ * This package contains an implementation of the Advertising ID Provider Service that supports
+ * IAdvertisingIdService.aidl for communication with the developer library, allowing you to
+ * easily make your own Advertising ID Provider. Simply do the following:
+ * <ol>
+ * <li>Implement the {@link AdvertisingIdProvider} interface in the provider library. Developer apps
+ * will be interacting with the provider through this programmatic interface.
+ * <li>Register the implementation by calling {@link #registerProviderCallable} within the
+ * provider’s {@link android.app.Application#onCreate} callback.
+ * <li>Register the Advertising Id settings UI with the intent filter
+ * "androidx.ads.identifier.provider.OPEN_SETTINGS".
+ * </ol>
+ */
+public class AdvertisingIdProviderManager {
+
+    @VisibleForTesting
+    static final String OPEN_SETTINGS_ACTION = "androidx.ads.identifier.provider.OPEN_SETTINGS";
+
+    private static Callable<AdvertisingIdProvider> sProviderCallable = null;
+
+    private AdvertisingIdProviderManager() {
+    }
+
+    /**
+     * Registers the {@link Callable} to create an instance of {@link AdvertisingIdProvider}.
+     *
+     * <p>This is used to lazy load the {@link AdvertisingIdProvider} when the Service is started.
+     * <p>This {@link Callable} will be called within the library's built-in Advertising ID
+     * Service's {@link android.app.Service#onCreate} method.
+     * <p>Provider could call this method to register the implementation in
+     * {@link android.app.Application#onCreate}, which is before
+     * {@link android.app.Service#onCreate} has been called.
+     */
+    public static void registerProviderCallable(
+            @NonNull Callable<AdvertisingIdProvider> providerCallable) {
+        sProviderCallable = Preconditions.checkNotNull(providerCallable);
+    }
+
+    /**
+     * Gets the {@link Callable} to create the Advertising ID Provider.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @Nullable
+    public static Callable<AdvertisingIdProvider> getProviderCallable() {
+        return sProviderCallable;
+    }
+
+    /** @hide */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @VisibleForTesting
+    public static void clearProviderCallable() {
+        sProviderCallable = null;
+    }
+
+    /**
+     * Retrieves a list of all the Advertising ID Providers' information on this device, including
+     * self and other providers which is based on the AndroidX Advertising ID Provider library.
+     *
+     * <p>This method helps one Advertising ID Provider find other providers. One usage of this is
+     * to link to other providers' settings activity from one provider's settings activity, so the
+     * user of the device can manager all the providers' settings together.
+     */
+    @NonNull
+    public static List<AdvertisingIdProviderInfo> getAdvertisingIdProviders(
+            @NonNull Context context) {
+        PackageManager packageManager = context.getPackageManager();
+        List<ResolveInfo> resolveInfos =
+                AdvertisingIdUtils.getAdvertisingIdProviderServices(packageManager);
+        if (resolveInfos.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        Map<String, String> activityMap = getOpenSettingsActivities(packageManager);
+        ServiceInfo highestPriorityServiceInfo =
+                AdvertisingIdUtils.selectServiceByPriority(resolveInfos, packageManager);
+
+        List<AdvertisingIdProviderInfo> providerInfos = new ArrayList<>();
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            String packageName = resolveInfo.serviceInfo.packageName;
+
+            AdvertisingIdProviderInfo.Builder builder =
+                    AdvertisingIdProviderInfo.builder()
+                            .setPackageName(packageName)
+                            .setHighestPriority(
+                                    resolveInfo.serviceInfo == highestPriorityServiceInfo);
+            String activityName = activityMap.get(packageName);
+            if (activityName != null) {
+                builder.setSettingsIntent(
+                        new Intent(OPEN_SETTINGS_ACTION).setClassName(packageName, activityName));
+            }
+            providerInfos.add(builder.build());
+        }
+        return providerInfos;
+    }
+
+    /**
+     * Retrieves a {@link Map} from package name to settings activity name.
+     *
+     * <p>This is achieved by looking up which activities can handle {@link #OPEN_SETTINGS_ACTION}
+     * intent action.
+     */
+    private static Map<String, String> getOpenSettingsActivities(PackageManager packageManager) {
+        Intent settingsIntent = new Intent(OPEN_SETTINGS_ACTION);
+        List<ResolveInfo> settingsResolveInfos = packageManager.queryIntentActivities(
+                settingsIntent, 0);
+        if (settingsResolveInfos.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        Map<String, String> activityMap = new HashMap<>();
+        for (ResolveInfo settingsResolveInfo : settingsResolveInfos) {
+            ActivityInfo settingsActivityInfo = settingsResolveInfo.activityInfo;
+            activityMap.put(settingsActivityInfo.packageName, settingsActivityInfo.name);
+        }
+        return activityMap;
+    }
+}
diff --git a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/AdvertisingIdAidlServiceImpl.java b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/AdvertisingIdAidlServiceImpl.java
new file mode 100644
index 0000000..c12a915
--- /dev/null
+++ b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/AdvertisingIdAidlServiceImpl.java
@@ -0,0 +1,43 @@
+/*
+ * 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.ads.identifier.provider.internal;
+
+import androidx.ads.identifier.provider.AdvertisingIdProvider;
+import androidx.ads.identifier.provider.IAdvertisingIdService;
+
+/**
+ * The implementation of the IAdvertisingIdService.aidl which retrieves values from
+ * {@link AdvertisingIdProvider} and replies to the client.
+ */
+class AdvertisingIdAidlServiceImpl extends IAdvertisingIdService.Stub {
+
+    private final AdvertisingIdProvider mProvider;
+
+    AdvertisingIdAidlServiceImpl(AdvertisingIdProvider advertisingIdProvider) {
+        mProvider = advertisingIdProvider;
+    }
+
+    @Override
+    public String getId() {
+        return mProvider.getId();
+    }
+
+    @Override
+    public boolean isLimitAdTrackingEnabled() {
+        return mProvider.isLimitAdTrackingEnabled();
+    }
+}
diff --git a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/AdvertisingIdService.java b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/AdvertisingIdService.java
new file mode 100644
index 0000000..45ad580
--- /dev/null
+++ b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/AdvertisingIdService.java
@@ -0,0 +1,68 @@
+/*
+ * 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.ads.identifier.provider.internal;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import androidx.ads.identifier.provider.AdvertisingIdProvider;
+import androidx.ads.identifier.provider.AdvertisingIdProviderManager;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.concurrent.Callable;
+
+/**
+ * The internal service of AndroidX Advertising ID Provider library to provide the Advertising ID.
+ */
+public class AdvertisingIdService extends Service {
+
+    private AdvertisingIdAidlServiceImpl mAdvertisingIdAidlServiceImpl;
+
+    @Override
+    public void onCreate() {
+        mAdvertisingIdAidlServiceImpl =
+                new AdvertisingIdAidlServiceImpl(getAdvertisingIdProvider());
+    }
+
+    @Override
+    @NonNull
+    public IBinder onBind(@NonNull Intent intent) {
+        return mAdvertisingIdAidlServiceImpl;
+    }
+
+    @VisibleForTesting
+    @NonNull
+    static AdvertisingIdProvider getAdvertisingIdProvider() {
+        Callable<AdvertisingIdProvider> providerCallable =
+                AdvertisingIdProviderManager.getProviderCallable();
+        if (providerCallable == null) {
+            throw new IllegalStateException("Advertising ID Provider not registered.");
+        }
+        AdvertisingIdProvider advertisingIdProvider;
+        try {
+            advertisingIdProvider = providerCallable.call();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not fetch the Advertising ID Provider.", e);
+        }
+        if (advertisingIdProvider == null) {
+            throw new IllegalArgumentException("Fetched Advertising ID Provider is null.");
+        }
+        return advertisingIdProvider;
+    }
+}
diff --git a/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/package-info.java b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/package-info.java
new file mode 100644
index 0000000..9ffd5e1
--- /dev/null
+++ b/ads/ads-identifier-provider/src/main/java/androidx/ads/identifier/provider/internal/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+/**
+ * @hide
+ */
+@RestrictTo(LIBRARY)
+package androidx.ads.identifier.provider.internal;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.RestrictTo;
diff --git a/ads/ads-identifier/api/1.0.0-alpha01.txt b/ads/ads-identifier/api/1.0.0-alpha01.txt
index da4f6cc..20ed609 100644
--- a/ads/ads-identifier/api/1.0.0-alpha01.txt
+++ b/ads/ads-identifier/api/1.0.0-alpha01.txt
@@ -1 +1,21 @@
 // Signature format: 3.0
+package androidx.ads.identifier {
+
+  public class AdvertisingIdClient {
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.ads.identifier.AdvertisingIdInfo!> getAdvertisingIdInfo(android.content.Context);
+    method public static boolean isAdvertisingIdProviderAvailable(android.content.Context);
+  }
+
+  public abstract class AdvertisingIdInfo {
+    method public abstract String getId();
+    method public abstract String getProviderPackageName();
+    method public abstract boolean isLimitAdTrackingEnabled();
+  }
+
+  public class AdvertisingIdNotAvailableException extends java.lang.Exception {
+    ctor public AdvertisingIdNotAvailableException(String);
+    ctor public AdvertisingIdNotAvailableException(String, Throwable);
+  }
+
+}
+
diff --git a/ads/ads-identifier/api/current.txt b/ads/ads-identifier/api/current.txt
index da4f6cc..20ed609 100644
--- a/ads/ads-identifier/api/current.txt
+++ b/ads/ads-identifier/api/current.txt
@@ -1 +1,21 @@
 // Signature format: 3.0
+package androidx.ads.identifier {
+
+  public class AdvertisingIdClient {
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.ads.identifier.AdvertisingIdInfo!> getAdvertisingIdInfo(android.content.Context);
+    method public static boolean isAdvertisingIdProviderAvailable(android.content.Context);
+  }
+
+  public abstract class AdvertisingIdInfo {
+    method public abstract String getId();
+    method public abstract String getProviderPackageName();
+    method public abstract boolean isLimitAdTrackingEnabled();
+  }
+
+  public class AdvertisingIdNotAvailableException extends java.lang.Exception {
+    ctor public AdvertisingIdNotAvailableException(String);
+    ctor public AdvertisingIdNotAvailableException(String, Throwable);
+  }
+
+}
+
diff --git a/ads/ads-identifier/api/restricted_1.0.0-alpha01.txt b/ads/ads-identifier/api/restricted_1.0.0-alpha01.txt
index da4f6cc..20ed609 100644
--- a/ads/ads-identifier/api/restricted_1.0.0-alpha01.txt
+++ b/ads/ads-identifier/api/restricted_1.0.0-alpha01.txt
@@ -1 +1,21 @@
 // Signature format: 3.0
+package androidx.ads.identifier {
+
+  public class AdvertisingIdClient {
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.ads.identifier.AdvertisingIdInfo!> getAdvertisingIdInfo(android.content.Context);
+    method public static boolean isAdvertisingIdProviderAvailable(android.content.Context);
+  }
+
+  public abstract class AdvertisingIdInfo {
+    method public abstract String getId();
+    method public abstract String getProviderPackageName();
+    method public abstract boolean isLimitAdTrackingEnabled();
+  }
+
+  public class AdvertisingIdNotAvailableException extends java.lang.Exception {
+    ctor public AdvertisingIdNotAvailableException(String);
+    ctor public AdvertisingIdNotAvailableException(String, Throwable);
+  }
+
+}
+
diff --git a/ads/ads-identifier/api/restricted_current.txt b/ads/ads-identifier/api/restricted_current.txt
index da4f6cc..20ed609 100644
--- a/ads/ads-identifier/api/restricted_current.txt
+++ b/ads/ads-identifier/api/restricted_current.txt
@@ -1 +1,21 @@
 // Signature format: 3.0
+package androidx.ads.identifier {
+
+  public class AdvertisingIdClient {
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.ads.identifier.AdvertisingIdInfo!> getAdvertisingIdInfo(android.content.Context);
+    method public static boolean isAdvertisingIdProviderAvailable(android.content.Context);
+  }
+
+  public abstract class AdvertisingIdInfo {
+    method public abstract String getId();
+    method public abstract String getProviderPackageName();
+    method public abstract boolean isLimitAdTrackingEnabled();
+  }
+
+  public class AdvertisingIdNotAvailableException extends java.lang.Exception {
+    ctor public AdvertisingIdNotAvailableException(String);
+    ctor public AdvertisingIdNotAvailableException(String, Throwable);
+  }
+
+}
+
diff --git a/ads/ads-identifier/build.gradle b/ads/ads-identifier/build.gradle
index e3a1410..3ab81a6 100644
--- a/ads/ads-identifier/build.gradle
+++ b/ads/ads-identifier/build.gradle
@@ -26,7 +26,23 @@
 }
 
 dependencies {
+    implementation("androidx.annotation:annotation:1.1.0")
+    implementation("androidx.core:core:1.1.0")
+    implementation(AUTO_VALUE_ANNOTATIONS)
+    annotationProcessor(AUTO_VALUE)
+    api(GUAVA_LISTENABLE_FUTURE)
+    implementation("androidx.concurrent:concurrent-futures:1.0.0-beta01")
 
+    implementation(project(":ads-identifier-common"))
+
+    androidTestImplementation(JUNIT)
+    androidTestImplementation(TRUTH)
+    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    androidTestImplementation(ANDROIDX_TEST_CORE)
+    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    androidTestImplementation(ANDROIDX_TEST_RULES)
 }
 
 android {
@@ -37,7 +53,7 @@
 
 androidx {
     name = "AndroidX Ads Identifier"
-    publish = Publish.NONE
+    publish = Publish.SNAPSHOT_AND_RELEASE
     mavenVersion = LibraryVersions.ADS_IDENTIFIER
     mavenGroup = LibraryGroups.ADS
     inceptionYear = "2019"
diff --git a/ads/ads-identifier/integration-tests/testapp/build.gradle b/ads/ads-identifier/integration-tests/testapp/build.gradle
new file mode 100644
index 0000000..c0fd225
--- /dev/null
+++ b/ads/ads-identifier/integration-tests/testapp/build.gradle
@@ -0,0 +1,35 @@
+/*
+ * 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 static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.application")
+}
+
+android {
+    defaultConfig {
+        applicationId "androidx.ads.identifier.testapp"
+        minSdkVersion 14
+    }
+}
+
+dependencies {
+    implementation(project(":ads-identifier"))
+    implementation(project(":ads-identifier-common"))
+    implementation(GUAVA_ANDROID)
+}
diff --git a/ads/ads-identifier/integration-tests/testapp/src/main/AndroidManifest.xml b/ads/ads-identifier/integration-tests/testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b9efcc2
--- /dev/null
+++ b/ads/ads-identifier/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="androidx.ads.identifier.testapp">
+
+    <application
+        android:allowBackup="false"
+        android:label="@string/app_name"
+        tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon">
+        <activity
+            android:name=".AdsIdentifierActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/ads/ads-identifier/integration-tests/testapp/src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.java b/ads/ads-identifier/integration-tests/testapp/src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.java
new file mode 100644
index 0000000..037200b
--- /dev/null
+++ b/ads/ads-identifier/integration-tests/testapp/src/main/java/androidx/ads/identifier/testapp/AdsIdentifierActivity.java
@@ -0,0 +1,146 @@
+/*
+ * 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.ads.identifier.testapp;
+
+import android.app.Activity;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.text.format.DateFormat;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.ads.identifier.AdvertisingIdClient;
+import androidx.ads.identifier.AdvertisingIdInfo;
+import androidx.ads.identifier.AdvertisingIdUtils;
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Simple activity as an ads identifier developer.
+ */
+public class AdsIdentifierActivity extends Activity {
+
+    private static final String HIGH_PRIORITY_PERMISSION =
+            "androidx.ads.identifier.provider.HIGH_PRIORITY";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_ads_identifier);
+    }
+
+    /** Gets Advertising ID. */
+    public void getId(View view) {
+        TextView textView = findViewById(R.id.text);
+        ListenableFuture<AdvertisingIdInfo> advertisingIdInfoListenableFuture =
+                AdvertisingIdClient.getAdvertisingIdInfo(getApplicationContext());
+        Futures.addCallback(advertisingIdInfoListenableFuture,
+                new FutureCallback<AdvertisingIdInfo>() {
+                    @Override
+                    public void onSuccess(AdvertisingIdInfo advertisingIdInfo) {
+                        runOnUiThread(() -> textView.setText(advertisingIdInfo.toString()));
+                    }
+
+                    @Override
+                    public void onFailure(Throwable throwable) {
+                        runOnUiThread(() -> textView.setText(throwable.toString()));
+                    }
+                }, MoreExecutors.directExecutor());
+    }
+
+    /** Gets Advertising ID synchronously. */
+    public void getIdSync(View view) {
+        TextView textView = findViewById(R.id.text);
+        new Thread(() -> {
+            AdvertisingIdInfo advertisingIdInfo;
+            try {
+                advertisingIdInfo =
+                        AdvertisingIdClient.getAdvertisingIdInfo(getApplicationContext()).get();
+            } catch (ExecutionException e) {
+                Throwable cause = e.getCause() != null ? e.getCause() : e;
+                runOnUiThread(() -> textView.setText(cause.toString()));
+                return;
+
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                runOnUiThread(() -> textView.setText(e.toString()));
+                return;
+            }
+            runOnUiThread(() -> textView.setText(advertisingIdInfo.toString()));
+        }).start();
+    }
+
+    /** Checks is provider available. */
+    public void isProviderAvailable(View view) {
+        TextView textView = findViewById(R.id.text);
+        boolean isAvailable = AdvertisingIdClient.isAdvertisingIdProviderAvailable(this);
+        textView.setText(String.valueOf(isAvailable));
+    }
+
+    /** Lists all the providers. */
+    public void listProvider(View view) {
+        TextView textView = findViewById(R.id.text);
+        textView.setText("Services:\n");
+
+        List<ResolveInfo> resolveInfos =
+                AdvertisingIdUtils.getAdvertisingIdProviderServices(getPackageManager());
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            String packageName = resolveInfo.serviceInfo.packageName;
+            PackageInfo packageInfo;
+            try {
+                packageInfo = getPackageManager().getPackageInfo(packageName,
+                        PackageManager.GET_PERMISSIONS);
+            } catch (PackageManager.NameNotFoundException e) {
+                continue;
+            }
+            show(textView, packageInfo);
+        }
+    }
+
+    private void show(TextView textView, PackageInfo packageInfo) {
+        textView.append(String.format(Locale.US, "%s\nFLAG_SYSTEM:%d\n",
+                packageInfo.packageName,
+                packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM));
+        textView.append(String.format(Locale.US, "isRequestHighPriority:%s\n",
+                isRequestHighPriority(packageInfo.requestedPermissions)));
+        textView.append(String.format(Locale.US, "firstInstallTime:%s\n",
+                DateFormat.format("yyyy-MM-dd HH:mm:ss", packageInfo.firstInstallTime)));
+        textView.append("\n");
+    }
+
+    private static boolean isRequestHighPriority(String[] array) {
+        if (array == null) {
+            return false;
+        }
+        for (String permission : array) {
+            if (HIGH_PRIORITY_PERMISSION.equals(permission)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/ads/ads-identifier/integration-tests/testapp/src/main/res/layout/activity_ads_identifier.xml b/ads/ads-identifier/integration-tests/testapp/src/main/res/layout/activity_ads_identifier.xml
new file mode 100644
index 0000000..0f4f3ea
--- /dev/null
+++ b/ads/ads-identifier/integration-tests/testapp/src/main/res/layout/activity_ads_identifier.xml
@@ -0,0 +1,74 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    tools:context="androidx.ads.identifier.testapp.AdsIdentifierActivity">
+
+    <LinearLayout
+        style="?android:attr/buttonBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:onClick="getId"
+            android:text="@string/get_ad_id" />
+
+        <Button
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:onClick="getIdSync"
+            android:text="@string/get_ad_id_sync" />
+    </LinearLayout>
+
+    <LinearLayout
+        style="?android:attr/buttonBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:onClick="isProviderAvailable"
+            android:text="@string/is_provider_available" />
+
+        <Button
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:onClick="listProvider"
+            android:text="@string/list_provider" />
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="" />
+</LinearLayout>
diff --git a/benchmark/src/androidTest/AndroidManifest.xml b/ads/ads-identifier/integration-tests/testapp/src/main/res/values/strings.xml
similarity index 65%
copy from benchmark/src/androidTest/AndroidManifest.xml
copy to ads/ads-identifier/integration-tests/testapp/src/main/res/values/strings.xml
index f5ec776..4d7dca0 100644
--- a/benchmark/src/androidTest/AndroidManifest.xml
+++ b/ads/ads-identifier/integration-tests/testapp/src/main/res/values/strings.xml
@@ -14,13 +14,11 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<manifest
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="androidx.benchmark.test">
 
-    <application
-        android:name="androidx.benchmark.ArgumentInjectingApplication">
-        <activity android:name="android.app.Activity"/>
-    </application>
-</manifest>
\ No newline at end of file
+<resources>
+    <string name="app_name">Ad ID</string>
+    <string name="get_ad_id">Get Ad ID</string>
+    <string name="get_ad_id_sync">Get Ad ID (Sync)</string>
+    <string name="list_provider">List Providers</string>
+    <string name="is_provider_available">Is Provider Available</string>
+</resources>
\ No newline at end of file
diff --git a/ads/ads-identifier/src/androidTest/AndroidManifest.xml b/ads/ads-identifier/src/androidTest/AndroidManifest.xml
index f5c3bd7..b4a88ac 100644
--- a/ads/ads-identifier/src/androidTest/AndroidManifest.xml
+++ b/ads/ads-identifier/src/androidTest/AndroidManifest.xml
@@ -15,5 +15,32 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="androidx.ads.identifier.tests">
+    xmlns:tools="http://schemas.android.com/tools"
+    package="androidx.ads.identifier.test">
+
+    <application>
+        <service
+            android:name="androidx.ads.identifier.MockAdvertisingIdService"
+            android:enabled="true"
+            android:exported="true"
+            android:process=":test"
+            tools:ignore="ExportedService">
+            <intent-filter>
+                <action android:name="androidx.ads.identifier.provider.GET_AD_ID" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </service>
+
+        <service
+            android:name="androidx.ads.identifier.MockAdvertisingIdThrowsNpeService"
+            android:enabled="true"
+            android:exported="true"
+            android:process=":test"
+            tools:ignore="ExportedService">
+            <intent-filter>
+                <action android:name="androidx.ads.identifier.provider.GET_AD_ID" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </service>
+    </application>
 </manifest>
diff --git a/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/AdvertisingIdClientTest.java b/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/AdvertisingIdClientTest.java
new file mode 100644
index 0000000..13706d1
--- /dev/null
+++ b/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/AdvertisingIdClientTest.java
@@ -0,0 +1,323 @@
+/*
+ * 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.ads.identifier;
+
+import static androidx.ads.identifier.AdvertisingIdUtils.GET_AD_ID_ACTION;
+import static androidx.ads.identifier.MockAdvertisingIdService.TESTING_AD_ID;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Build;
+
+import androidx.ads.identifier.internal.BlockingServiceConnection;
+import androidx.ads.identifier.provider.IAdvertisingIdService;
+import androidx.annotation.NonNull;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class AdvertisingIdClientTest {
+    private static final String MOCK_SERVICE_PACKAGE_NAME = "androidx.ads.identifier.test";
+    private static final String MOCK_SERVICE_NAME = MockAdvertisingIdService.class.getName();
+    private static final String MOCK_THROWS_NPE_SERVICE_NAME =
+            MockAdvertisingIdThrowsNpeService.class.getName();
+
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock
+    private PackageManager mMockPackageManager;
+
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        MockAdvertisingIdClient.sGetServiceConnectionThrowException = false;
+        MockAdvertisingIdClient.sGetAdvertisingIdServiceThrowInterruptedException = false;
+
+        Context applicationContext = ApplicationProvider.getApplicationContext();
+
+        mContext = new ContextWrapper(applicationContext) {
+            @Override
+            public Context getApplicationContext() {
+                return this;
+            }
+
+            @Override
+            public PackageManager getPackageManager() {
+                return mMockPackageManager;
+            }
+        };
+
+        mockQueryIntentServices(Lists.newArrayList(
+                createResolveInfo(MOCK_SERVICE_PACKAGE_NAME, MOCK_SERVICE_NAME)));
+    }
+
+    @After
+    public void tearDown() {
+        Intent serviceIntent = new Intent(GET_AD_ID_ACTION);
+        serviceIntent.setClassName(MOCK_SERVICE_PACKAGE_NAME, MOCK_SERVICE_NAME);
+        mContext.stopService(serviceIntent);
+
+        Intent npeServiceIntent = new Intent(GET_AD_ID_ACTION);
+        npeServiceIntent.setClassName(MOCK_SERVICE_PACKAGE_NAME, MOCK_THROWS_NPE_SERVICE_NAME);
+        mContext.stopService(npeServiceIntent);
+    }
+
+    private void mockQueryIntentServices(List<ResolveInfo> resolveInfos) throws Exception {
+        boolean supportMatchSystemOnly = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
+        int flags = supportMatchSystemOnly ? PackageManager.MATCH_SYSTEM_ONLY : 0;
+        when(mMockPackageManager.queryIntentServices(
+                argThat(intent -> intent != null && GET_AD_ID_ACTION.equals(intent.getAction())),
+                eq(flags))).thenReturn(resolveInfos);
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            if (!supportMatchSystemOnly) {
+                ApplicationInfo applicationInfo = new ApplicationInfo();
+                applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+                when(mMockPackageManager.getApplicationInfo(resolveInfo.serviceInfo.packageName, 0))
+                        .thenReturn(applicationInfo);
+            }
+        }
+    }
+
+    @Test
+    public void getAdvertisingIdInfo() throws Exception {
+        AdvertisingIdInfo info = AdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
+
+        assertThat(info).isEqualTo(AdvertisingIdInfo.builder()
+                .setId(TESTING_AD_ID)
+                .setLimitAdTrackingEnabled(true)
+                .setProviderPackageName(MOCK_SERVICE_PACKAGE_NAME)
+                .build());
+    }
+
+    public void getAdvertisingIdInfo_noProvider() throws Exception {
+        mockQueryIntentServices(Collections.emptyList());
+
+        try {
+            AdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
+        } catch (ExecutionException e) {
+            assertThat(e).hasCauseThat().isInstanceOf(AdvertisingIdNotAvailableException.class);
+            return;
+        }
+        fail("Expected ExecutionException");
+    }
+
+    @Test
+    public void getAdvertisingIdInfo_serviceThrowsNullPointerException() throws Exception {
+        mockQueryIntentServices(Lists.newArrayList(
+                createResolveInfo(MOCK_SERVICE_PACKAGE_NAME, MOCK_THROWS_NPE_SERVICE_NAME)));
+
+        try {
+            AdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
+        } catch (ExecutionException e) {
+            assertThat(e).hasCauseThat().isInstanceOf(AdvertisingIdNotAvailableException.class);
+            return;
+        }
+        fail("Expected ExecutionException");
+    }
+
+    @Test
+    public void getInfo_getInfoTwice() throws Exception {
+        AdvertisingIdClient client = new AdvertisingIdClient(mContext);
+        client.getInfoInternal();
+        AdvertisingIdInfo info = client.getInfoInternal();
+        client.finish();
+
+        assertThat(info).isEqualTo(AdvertisingIdInfo.builder()
+                .setId(TESTING_AD_ID)
+                .setLimitAdTrackingEnabled(true)
+                .setProviderPackageName(MOCK_SERVICE_PACKAGE_NAME)
+                .build());
+    }
+
+    @Test
+    public void getInfo_twoClients() throws Exception {
+        AdvertisingIdClient client1 = new AdvertisingIdClient(mContext);
+        AdvertisingIdClient client2 = new AdvertisingIdClient(mContext);
+        AdvertisingIdInfo info1 = client1.getInfoInternal();
+        AdvertisingIdInfo info2 = client1.getInfoInternal();
+        client1.finish();
+        client2.finish();
+
+        AdvertisingIdInfo expected = AdvertisingIdInfo.builder()
+                .setId(TESTING_AD_ID)
+                .setLimitAdTrackingEnabled(true)
+                .setProviderPackageName(MOCK_SERVICE_PACKAGE_NAME)
+                .build();
+        assertThat(info1).isEqualTo(expected);
+        assertThat(info2).isEqualTo(expected);
+    }
+
+    @Test(timeout = 11000L)
+    public void getAdvertisingIdInfo_connectionTimeout() throws Exception {
+        try {
+            MockAdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
+        } catch (ExecutionException e) {
+            assertThat(e).hasCauseThat().isInstanceOf(TimeoutException.class);
+            return;
+        }
+        fail("Expected ExecutionException");
+    }
+
+    @Test
+    public void getAdvertisingIdInfo_interrupted() throws Exception {
+        MockAdvertisingIdClient.sGetAdvertisingIdServiceThrowInterruptedException = true;
+
+        try {
+            MockAdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
+        } catch (ExecutionException e) {
+            assertThat(e).hasCauseThat().isInstanceOf(InterruptedException.class);
+            return;
+        }
+        fail("Expected ExecutionException");
+    }
+
+    @Test
+    public void getAdvertisingIdInfo_connectionFailed() throws Exception {
+        MockAdvertisingIdClient.sGetServiceConnectionThrowException = true;
+
+        try {
+            MockAdvertisingIdClient.getAdvertisingIdInfo(mContext).get();
+        } catch (ExecutionException e) {
+            assertThat(e).hasCauseThat().isInstanceOf(IOException.class);
+            return;
+        }
+        fail("Expected ExecutionException");
+    }
+
+    private static class MockAdvertisingIdClient extends AdvertisingIdClient {
+
+        static boolean sGetServiceConnectionThrowException = false;
+        static boolean sGetAdvertisingIdServiceThrowInterruptedException = false;
+
+        static Thread sCurrentThread;
+
+        MockAdvertisingIdClient(Context context) {
+            super(context);
+        }
+
+        @Override
+        BlockingServiceConnection getServiceConnection() throws IOException {
+            if (sGetServiceConnectionThrowException) {
+                throw new IOException();
+            }
+
+            sCurrentThread = Thread.currentThread();
+
+            // This connection does not bind to any service, so it always timeout.
+            return new BlockingServiceConnection();
+        }
+
+        @Override
+        IAdvertisingIdService getAdvertisingIdService(BlockingServiceConnection bsc)
+                throws TimeoutException, InterruptedException {
+            if (sGetAdvertisingIdServiceThrowInterruptedException) {
+                throw new InterruptedException();
+            }
+            return super.getAdvertisingIdService(bsc);
+        }
+
+        @NonNull
+        public static ListenableFuture<AdvertisingIdInfo> getAdvertisingIdInfo(
+                @NonNull Context context) {
+            return CallbackToFutureAdapter.getFuture(completer -> {
+                EXECUTOR_SERVICE.execute(() -> {
+                    MockAdvertisingIdClient client = new MockAdvertisingIdClient(context);
+                    try {
+                        completer.set(client.getInfoInternal());
+                    } catch (IOException | AdvertisingIdNotAvailableException | TimeoutException
+                            | InterruptedException e) {
+                        completer.setException(e);
+                    }
+                    // No need to call unbindService() here since not call bindService() in this
+                    // mock.
+                });
+                return "getAdvertisingIdInfo";
+            });
+        }
+    }
+
+    @Test
+    public void normalizeId() throws Exception {
+        String id = AdvertisingIdClient.normalizeId("abc");
+
+        assertThat(id).isEqualTo("90015098-3cd2-3fb0-9696-3f7d28e17f72"); // UUID version 3 of "abc"
+    }
+
+    @Test
+    public void isAdvertisingIdProviderAvailable() {
+        assertThat(AdvertisingIdClient.isAdvertisingIdProviderAvailable(mContext)).isTrue();
+    }
+
+    @Test
+    public void isAdvertisingIdProviderAvailable_noProvider() throws Exception {
+        mockQueryIntentServices(Collections.emptyList());
+
+        assertThat(AdvertisingIdClient.isAdvertisingIdProviderAvailable(mContext)).isFalse();
+    }
+
+    @Test
+    public void isAdvertisingIdProviderAvailable_twoProviders() throws Exception {
+        mockQueryIntentServices(Lists.newArrayList(
+                createResolveInfo("com.a", "A"),
+                createResolveInfo("com.b", "B")));
+
+        assertThat(AdvertisingIdClient.isAdvertisingIdProviderAvailable(mContext)).isTrue();
+    }
+
+    private static ResolveInfo createResolveInfo(String packageName, String name) {
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.serviceInfo = new ServiceInfo();
+        resolveInfo.serviceInfo.packageName = packageName;
+        resolveInfo.serviceInfo.name = name;
+        return resolveInfo;
+    }
+}
diff --git a/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/MockAdvertisingIdService.java b/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/MockAdvertisingIdService.java
new file mode 100644
index 0000000..24bdf9b
--- /dev/null
+++ b/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/MockAdvertisingIdService.java
@@ -0,0 +1,59 @@
+/*
+ * 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.ads.identifier;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import androidx.ads.identifier.provider.IAdvertisingIdService;
+import androidx.annotation.Nullable;
+
+/**
+ * Provide a mock for {@link androidx.ads.identifier.provider.IAdvertisingIdService}.
+ * To be used in unit tests.
+ */
+public class MockAdvertisingIdService extends Service {
+
+    static final String TESTING_AD_ID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
+
+    private MockAdvertisingIdServiceImpl mAdvertisingIdServiceImpl;
+
+    @Override
+    public void onCreate() {
+        mAdvertisingIdServiceImpl = new MockAdvertisingIdServiceImpl();
+    }
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mAdvertisingIdServiceImpl;
+    }
+
+    private static class MockAdvertisingIdServiceImpl extends IAdvertisingIdService.Stub {
+        @Override
+        public String getId() throws RemoteException {
+            return TESTING_AD_ID;
+        }
+
+        @Override
+        public boolean isLimitAdTrackingEnabled() throws RemoteException {
+            return true;
+        }
+    }
+}
diff --git a/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/MockAdvertisingIdThrowsNpeService.java b/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/MockAdvertisingIdThrowsNpeService.java
new file mode 100644
index 0000000..d6c7f16
--- /dev/null
+++ b/ads/ads-identifier/src/androidTest/java/androidx/ads/identifier/MockAdvertisingIdThrowsNpeService.java
@@ -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.ads.identifier;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import androidx.ads.identifier.provider.IAdvertisingIdService;
+import androidx.annotation.Nullable;
+
+/**
+ * Provide a mock for {@link IAdvertisingIdService} which always throw {@link NullPointerException}.
+ * To be used in unit tests.
+ */
+public class MockAdvertisingIdThrowsNpeService extends Service {
+
+    private MockAdvertisingIdServiceImpl mAdvertisingIdServiceImpl;
+
+    @Override
+    public void onCreate() {
+        mAdvertisingIdServiceImpl = new MockAdvertisingIdServiceImpl();
+    }
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mAdvertisingIdServiceImpl;
+    }
+
+    private static class MockAdvertisingIdServiceImpl extends IAdvertisingIdService.Stub {
+        @Override
+        public String getId() throws RemoteException {
+            throw new NullPointerException();
+        }
+
+        @Override
+        public boolean isLimitAdTrackingEnabled() throws RemoteException {
+            throw new NullPointerException();
+        }
+    }
+}
diff --git a/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdClient.java b/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdClient.java
new file mode 100644
index 0000000..5993e0e
--- /dev/null
+++ b/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdClient.java
@@ -0,0 +1,268 @@
+/*
+ * 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.ads.identifier;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+
+import androidx.ads.identifier.internal.BlockingServiceConnection;
+import androidx.ads.identifier.provider.IAdvertisingIdService;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.annotation.WorkerThread;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.core.util.Preconditions;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Locale;
+import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Client for retrieving Advertising ID related info from an AndroidX ID Provider installed on
+ * the device.
+ *
+ * <p>Typical usage would be:
+ * <ol>
+ * <li>Call {@link #isAdvertisingIdProviderAvailable} to make sure there is an Advertising ID
+ * Provider available.
+ * <li>Call {@link #getAdvertisingIdInfo} to get Advertising ID info (the Advertising ID and LAT
+ * setting).
+ * </ol>
+ */
+public class AdvertisingIdClient {
+
+    private static final long SERVICE_CONNECTION_TIMEOUT_SECONDS = 10;
+
+    @VisibleForTesting
+    static final ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool();
+
+    @Nullable
+    private BlockingServiceConnection mConnection;
+
+    @Nullable
+    private IAdvertisingIdService mService;
+
+    private final Context mContext;
+
+    private ComponentName mComponentName;
+
+    /** Constructs a new {@link AdvertisingIdClient} object. */
+    @VisibleForTesting
+    AdvertisingIdClient(Context context) {
+        Preconditions.checkNotNull(context);
+        mContext = context.getApplicationContext();
+    }
+
+    @WorkerThread
+    private void start() throws IOException, AdvertisingIdNotAvailableException, TimeoutException,
+            InterruptedException {
+        if (mConnection == null) {
+            mComponentName = getProviderComponentName(mContext);
+            mConnection = getServiceConnection();
+            mService = getAdvertisingIdService(mConnection);
+        }
+    }
+
+    /** Returns the Advertising ID info as {@link AdvertisingIdInfo}. */
+    @VisibleForTesting
+    @WorkerThread
+    AdvertisingIdInfo getInfoInternal() throws IOException, AdvertisingIdNotAvailableException,
+            TimeoutException, InterruptedException {
+        if (mConnection == null) {
+            start();
+        }
+        try {
+            String id = mService.getId();
+            if (id == null || id.trim().isEmpty()) {
+                throw new AdvertisingIdNotAvailableException(
+                        "Advertising ID Provider does not returns an Advertising ID.");
+            }
+            return AdvertisingIdInfo.builder()
+                    .setId(normalizeId(id))
+                    .setProviderPackageName(mComponentName.getPackageName())
+                    .setLimitAdTrackingEnabled(mService.isLimitAdTrackingEnabled())
+                    .build();
+        } catch (RemoteException e) {
+            throw new IOException("Remote exception", e);
+        } catch (RuntimeException e) {
+            throw new AdvertisingIdNotAvailableException(
+                    "Advertising ID Provider throws a exception.", e);
+        }
+    }
+
+    /**
+     * Checks the Advertising ID format, if it's not in UUID format, normalizes the Advertising
+     * ID to UUID format.
+     *
+     * @return Advertising ID, in lower case format using locale {@code Locale.US};
+     */
+    @VisibleForTesting
+    static String normalizeId(String id) {
+        String lowerCaseId = id.toLowerCase(Locale.US);
+        if (isUuidFormat(lowerCaseId)) {
+            return lowerCaseId;
+        }
+        return UUID.nameUUIDFromBytes(id.getBytes(Charset.forName("UTF-8"))).toString();
+    }
+
+    /* Validate the input is lowercase and is a valid UUID. */
+    private static boolean isUuidFormat(String id) {
+        try {
+            return id.equals(UUID.fromString(id).toString());
+        } catch (IllegalArgumentException iae) {
+            return false;
+        }
+    }
+
+    /** Closes the connection to the Advertising ID Provider Service. */
+    @VisibleForTesting
+    void finish() {
+        if (mConnection == null) {
+            return;
+        }
+        mContext.unbindService(mConnection);
+        mComponentName = null;
+        mConnection = null;
+        mService = null;
+    }
+
+    private static ComponentName getProviderComponentName(Context context)
+            throws AdvertisingIdNotAvailableException {
+        PackageManager packageManager = context.getPackageManager();
+        List<ResolveInfo> resolveInfos =
+                AdvertisingIdUtils.getAdvertisingIdProviderServices(packageManager);
+        ServiceInfo serviceInfo =
+                AdvertisingIdUtils.selectServiceByPriority(resolveInfos, packageManager);
+        if (serviceInfo == null) {
+            throw new AdvertisingIdNotAvailableException("No Advertising ID Provider available.");
+        }
+        return new ComponentName(serviceInfo.packageName, serviceInfo.name);
+    }
+
+    /**
+     * Retrieves BlockingServiceConnection which must be unbound after use.
+     *
+     * @throws IOException when unable to bind service successfully.
+     */
+    @VisibleForTesting
+    BlockingServiceConnection getServiceConnection() throws IOException {
+        Intent intent = new Intent(AdvertisingIdUtils.GET_AD_ID_ACTION);
+        intent.setComponent(mComponentName);
+
+        BlockingServiceConnection bsc = new BlockingServiceConnection();
+        if (mContext.bindService(intent, bsc, Service.BIND_AUTO_CREATE)) {
+            return bsc;
+        } else {
+            throw new IOException("Connection failure");
+        }
+    }
+
+    /**
+     * Get the {@link IAdvertisingIdService} from the blocking queue. This should wait until
+     * {@link android.content.ServiceConnection#onServiceConnected} event with a
+     * {@link #SERVICE_CONNECTION_TIMEOUT_SECONDS} second timeout.
+     *
+     * @throws TimeoutException     if connection timeout period has expired.
+     * @throws InterruptedException if connection has been interrupted before connected.
+     */
+    @VisibleForTesting
+    @WorkerThread
+    IAdvertisingIdService getAdvertisingIdService(BlockingServiceConnection bsc)
+            throws TimeoutException, InterruptedException {
+        // Block until the bind is complete, or timeout period is over.
+        return IAdvertisingIdService.Stub.asInterface(
+                bsc.getServiceWithTimeout(
+                        SERVICE_CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS));
+    }
+
+    /**
+     * Checks whether there is any Advertising ID Provider installed on the device.
+     *
+     * <p>This method does a quick check for the Advertising ID providers.
+     * <p>Note: Even if this method returns true, there is still a possibility that the
+     * {@link #getAdvertisingIdInfo(Context)} method throws an exception for some reason.
+     *
+     * @param context Current {@link Context} (such as the current {@link android.app.Activity}).
+     * @return whether there is an Advertising ID Provider available on the device.
+     */
+    public static boolean isAdvertisingIdProviderAvailable(@NonNull Context context) {
+        return !AdvertisingIdUtils.getAdvertisingIdProviderServices(context.getPackageManager())
+                .isEmpty();
+    }
+
+    /**
+     * Retrieves the user's Advertising ID info.
+     *
+     * <p>When multiple Advertising ID Providers are installed on the device, this method will
+     * always return the Advertising ID information from same Advertising ID Provider for all
+     * apps which use this library, using following priority:
+     * <ol>
+     * <li>System-level providers with "androidx.ads.identifier.provider.HIGH_PRIORITY" permission
+     * <li>Other system-level providers
+     * </ol>
+     * <p>If there are ties in any of the above categories, it will use this priority:
+     * <ol>
+     * <li>First app by earliest install time
+     * ({@link android.content.pm.PackageInfo#firstInstallTime})
+     * <li>First app by package name alphabetically sorted
+     * </ol>
+     *
+     * @param context Current {@link Context} (such as the current {@link android.app.Activity}).
+     * @return A {@link ListenableFuture} that will be fulfilled with a {@link AdvertisingIdInfo}
+     * which contains the user's Advertising ID info, or rejected with the following exceptions,
+     * <ul>
+     * <li><b>IOException</b> signaling connection to Advertising ID Providers failed.
+     * <li><b>AdvertisingIdNotAvailableException</b> indicating Advertising ID is not available,
+     * like no Advertising ID Provider found or provider does not return an Advertising ID.
+     * <li><b>TimeoutException</b> indicating connection timeout period has expired.
+     * <li><b>InterruptedException</b> indicating the current thread has been interrupted.
+     * </ul>
+     */
+    @NonNull
+    public static ListenableFuture<AdvertisingIdInfo> getAdvertisingIdInfo(
+            @NonNull Context context) {
+        return CallbackToFutureAdapter.getFuture(completer -> {
+            EXECUTOR_SERVICE.execute(() -> {
+                AdvertisingIdClient client = new AdvertisingIdClient(context);
+                try {
+                    completer.set(client.getInfoInternal());
+                } catch (IOException | AdvertisingIdNotAvailableException | TimeoutException
+                        | InterruptedException e) {
+                    completer.setException(e);
+                } finally {
+                    client.finish();
+                }
+            });
+            return "getAdvertisingIdInfo";
+        });
+    }
+}
diff --git a/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdInfo.java b/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdInfo.java
new file mode 100644
index 0000000..6821731a
--- /dev/null
+++ b/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdInfo.java
@@ -0,0 +1,72 @@
+/*
+ * 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.ads.identifier;
+
+import androidx.annotation.NonNull;
+
+import com.google.auto.value.AutoValue;
+
+/**
+ * Advertising ID Information.
+ * Includes both the Advertising ID and the limit ad tracking setting.
+ */
+@AutoValue
+public abstract class AdvertisingIdInfo {
+
+    // Create a no-args constructor so it doesn't appear in current.txt
+    AdvertisingIdInfo() {
+    }
+
+    /**
+     * Retrieves the Advertising ID.
+     *
+     * <p>This will be in UUID format, either Advertising ID Provider provided an Advertising ID
+     * in UUID format, or this developer library will normalize the Advertising ID to UUID format.
+     */
+    @NonNull
+    public abstract String getId();
+
+    /** Retrieves the Advertising ID provider package name. */
+    @NonNull
+    public abstract String getProviderPackageName();
+
+    /** Retrieves whether the user has set Limit Advertising Tracking. */
+    public abstract boolean isLimitAdTrackingEnabled();
+
+
+    /** Create a {@link Builder}. */
+    static Builder builder() {
+        return new AutoValue_AdvertisingIdInfo.Builder();
+    }
+
+    /** The builder for {@link AdvertisingIdInfo}. */
+    @AutoValue.Builder
+    abstract static class Builder {
+
+        // Create a no-args constructor so it doesn't appear in current.txt
+        Builder() {
+        }
+
+        abstract Builder setId(String id);
+
+        abstract Builder setProviderPackageName(String providerPackageName);
+
+        abstract Builder setLimitAdTrackingEnabled(boolean limitAdTrackingEnabled);
+
+        abstract AdvertisingIdInfo build();
+    }
+}
diff --git a/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdNotAvailableException.java b/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdNotAvailableException.java
new file mode 100644
index 0000000..e698f1e
--- /dev/null
+++ b/ads/ads-identifier/src/main/java/androidx/ads/identifier/AdvertisingIdNotAvailableException.java
@@ -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.ads.identifier;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Indicates an AndroidX Advertising ID is not available.
+ */
+public class AdvertisingIdNotAvailableException extends Exception {
+    public AdvertisingIdNotAvailableException(@NonNull String message) {
+        super(message);
+    }
+
+    public AdvertisingIdNotAvailableException(@NonNull String message, @NonNull Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/ads/ads-identifier/src/main/java/androidx/ads/identifier/internal/BlockingServiceConnection.java b/ads/ads-identifier/src/main/java/androidx/ads/identifier/internal/BlockingServiceConnection.java
new file mode 100644
index 0000000..4963cbd
--- /dev/null
+++ b/ads/ads-identifier/src/main/java/androidx/ads/identifier/internal/BlockingServiceConnection.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.ads.identifier.internal;
+
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+
+import androidx.annotation.NonNull;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * A one-time use ServiceConnection that facilitates waiting for the bind to complete and the
+ * passing of the IBinder from the callback thread to the waiting thread.
+ */
+public class BlockingServiceConnection implements ServiceConnection {
+
+    // Facilitates passing of the IBinder across threads
+    private final BlockingQueue<IBinder> mBlockingQueue = new LinkedBlockingQueue<>();
+
+    @Override
+    public void onServiceConnected(@NonNull ComponentName name, @NonNull IBinder service) {
+        mBlockingQueue.add(service);
+    }
+
+    @Override
+    public void onServiceDisconnected(@NonNull ComponentName name) {
+        // Don't worry about clearing the returned binder in this case. If it does
+        // happen a RemoteException will be thrown, which is already handled.
+    }
+
+    /**
+     * Blocks until the bind is complete with a timeout and returns the bound IBinder. This must
+     * only be called once.
+     *
+     * @return the IBinder of the bound service
+     * @throws InterruptedException  if the current thread is interrupted while waiting for the bind
+     * @throws IllegalStateException if called more than once
+     * @throws TimeoutException      if the timeout period has elapsed
+     */
+    @NonNull
+    public IBinder getServiceWithTimeout(long timeout, @NonNull TimeUnit timeUnit)
+            throws InterruptedException, TimeoutException {
+        IBinder binder = mBlockingQueue.poll(timeout, timeUnit);
+        if (binder == null) {
+            throw new TimeoutException("Timed out waiting for the service connection");
+        } else {
+            return binder;
+        }
+    }
+}
diff --git a/ads/ads-identifier/src/main/java/androidx/ads/identifier/internal/package-info.java b/ads/ads-identifier/src/main/java/androidx/ads/identifier/internal/package-info.java
new file mode 100644
index 0000000..e9ad310
--- /dev/null
+++ b/ads/ads-identifier/src/main/java/androidx/ads/identifier/internal/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+/**
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+package androidx.ads.identifier.internal;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import androidx.annotation.RestrictTo;
diff --git a/animation/build.gradle b/animation/build.gradle
index 65781d0..1d1800e 100644
--- a/animation/build.gradle
+++ b/animation/build.gradle
@@ -26,7 +26,7 @@
 
 dependencies {
     implementation("androidx.annotation:annotation:1.1.0")
-    implementation("androidx.core:core:1.1.0-rc01")
+    implementation("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.1.0")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT, libs.exclude_for_espresso)
diff --git a/animation/integration-tests/testapp/build.gradle b/animation/integration-tests/testapp/build.gradle
index 82a6fe05..a9992a4 100644
--- a/animation/integration-tests/testapp/build.gradle
+++ b/animation/integration-tests/testapp/build.gradle
@@ -23,7 +23,7 @@
 
 dependencies {
     implementation("androidx.annotation:annotation:1.1.0")
-    implementation("androidx.core:core:1.1.0-rc01")
+    implementation("androidx.core:core:1.1.0")
     implementation(project(":animation"))
     implementation(project(":animation:testing"))
 
diff --git a/animation/testing/build.gradle b/animation/testing/build.gradle
index f33191e..ad98a4c 100644
--- a/animation/testing/build.gradle
+++ b/animation/testing/build.gradle
@@ -26,7 +26,7 @@
 
 dependencies {
     implementation("androidx.annotation:annotation:1.1.0")
-    implementation("androidx.core:core:1.1.0-rc01")
+    implementation("androidx.core:core:1.1.0")
     implementation(project(":animation"))
     implementation(ANDROIDX_TEST_EXT_JUNIT)
     implementation(ANDROIDX_TEST_CORE)
diff --git a/annotation/annotation-experimental-lint/build.gradle b/annotation/annotation-experimental-lint/build.gradle
index 6ef83b3..9fae7f7 100644
--- a/annotation/annotation-experimental-lint/build.gradle
+++ b/annotation/annotation-experimental-lint/build.gradle
@@ -44,11 +44,11 @@
 androidx {
     name = "Experimental annotation lint checks"
     toolingProject = true
-    publish = Publish.NONE
-    mavenVersion = LibraryVersions.ANNOTATION
+    publish = Publish.SNAPSHOT_AND_RELEASE
+    mavenVersion = LibraryVersions.ANNOTATION_EXPERIMENTAL
     mavenGroup = LibraryGroups.ANNOTATION
     inceptionYear = "2019"
-    description = "Lint checks for the experimental annotation library"
-    url = ARCHITECTURE_URL
+    description = "Lint checks for the Experimental annotation library. Also enforces the " +
+            "semantics of Kotlin @Experimental APIs from within Android Java source code."
     compilationTarget = CompilationTarget.HOST
 }
diff --git a/annotation/annotation-experimental/api/1.0.0-alpha01.txt b/annotation/annotation-experimental/api/1.0.0-alpha01.txt
new file mode 100644
index 0000000..fce9faf2
--- /dev/null
+++ b/annotation/annotation-experimental/api/1.0.0-alpha01.txt
@@ -0,0 +1,18 @@
+// Signature format: 3.0
+package androidx.annotation.experimental {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Experimental {
+    method public abstract androidx.annotation.experimental.Experimental.Level level() default androidx.annotation.experimental.Experimental.Level.ERROR;
+  }
+
+  public enum Experimental.Level {
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level ERROR;
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level WARNING;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface UseExperimental {
+    method public abstract Class<?> markerClass();
+  }
+
+}
+
diff --git a/annotation/annotation-experimental/api/current.txt b/annotation/annotation-experimental/api/current.txt
new file mode 100644
index 0000000..fce9faf2
--- /dev/null
+++ b/annotation/annotation-experimental/api/current.txt
@@ -0,0 +1,18 @@
+// Signature format: 3.0
+package androidx.annotation.experimental {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Experimental {
+    method public abstract androidx.annotation.experimental.Experimental.Level level() default androidx.annotation.experimental.Experimental.Level.ERROR;
+  }
+
+  public enum Experimental.Level {
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level ERROR;
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level WARNING;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface UseExperimental {
+    method public abstract Class<?> markerClass();
+  }
+
+}
+
diff --git a/benchmark/api/res-1.0.0-alpha01.txt b/annotation/annotation-experimental/api/res-1.0.0-alpha01.txt
similarity index 100%
copy from benchmark/api/res-1.0.0-alpha01.txt
copy to annotation/annotation-experimental/api/res-1.0.0-alpha01.txt
diff --git a/annotation/annotation-experimental/api/restricted_1.0.0-alpha01.txt b/annotation/annotation-experimental/api/restricted_1.0.0-alpha01.txt
new file mode 100644
index 0000000..fce9faf2
--- /dev/null
+++ b/annotation/annotation-experimental/api/restricted_1.0.0-alpha01.txt
@@ -0,0 +1,18 @@
+// Signature format: 3.0
+package androidx.annotation.experimental {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Experimental {
+    method public abstract androidx.annotation.experimental.Experimental.Level level() default androidx.annotation.experimental.Experimental.Level.ERROR;
+  }
+
+  public enum Experimental.Level {
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level ERROR;
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level WARNING;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface UseExperimental {
+    method public abstract Class<?> markerClass();
+  }
+
+}
+
diff --git a/annotation/annotation-experimental/api/restricted_current.txt b/annotation/annotation-experimental/api/restricted_current.txt
new file mode 100644
index 0000000..fce9faf2
--- /dev/null
+++ b/annotation/annotation-experimental/api/restricted_current.txt
@@ -0,0 +1,18 @@
+// Signature format: 3.0
+package androidx.annotation.experimental {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface Experimental {
+    method public abstract androidx.annotation.experimental.Experimental.Level level() default androidx.annotation.experimental.Experimental.Level.ERROR;
+  }
+
+  public enum Experimental.Level {
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level ERROR;
+    enum_constant public static final androidx.annotation.experimental.Experimental.Level WARNING;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface UseExperimental {
+    method public abstract Class<?> markerClass();
+  }
+
+}
+
diff --git a/annotation/annotation-experimental/build.gradle b/annotation/annotation-experimental/build.gradle
index 922edae..4d61d05 100644
--- a/annotation/annotation-experimental/build.gradle
+++ b/annotation/annotation-experimental/build.gradle
@@ -30,8 +30,10 @@
 androidx {
     name = "Experimental annotation"
     publish = Publish.SNAPSHOT_AND_RELEASE
-    mavenVersion = LibraryVersions.ANNOTATION
+    mavenVersion = LibraryVersions.ANNOTATION_EXPERIMENTAL
     mavenGroup = LibraryGroups.ANNOTATION
     inceptionYear = "2019"
-    description = "Annotation for use on unstable API surfaces"
+    description = "Java annotation for use on unstable Android API surfaces. When used in " +
+            "conjunction with the Experimental annotation lint checks, this annotation provides " +
+            "functional parity with Kotlin's Experimental annotation."
 }
diff --git a/appcompat/api/1.2.0-alpha01.txt b/appcompat/api/1.2.0-alpha01.txt
index e0c9d81..22cb389 100644
--- a/appcompat/api/1.2.0-alpha01.txt
+++ b/appcompat/api/1.2.0-alpha01.txt
@@ -424,8 +424,8 @@
 package androidx.appcompat.widget {
 
   public class ActionMenuView extends androidx.appcompat.widget.LinearLayoutCompat {
-    ctor public ActionMenuView(android.content.Context!);
-    ctor public ActionMenuView(android.content.Context!, android.util.AttributeSet!);
+    ctor public ActionMenuView(android.content.Context);
+    ctor public ActionMenuView(android.content.Context, android.util.AttributeSet?);
     method public void dismissPopupMenus();
     method protected androidx.appcompat.widget.ActionMenuView.LayoutParams! generateDefaultLayoutParams();
     method public androidx.appcompat.widget.ActionMenuView.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
@@ -460,9 +460,9 @@
   }
 
   public class AppCompatAutoCompleteTextView extends android.widget.AutoCompleteTextView implements androidx.core.view.TintableBackgroundView {
-    ctor public AppCompatAutoCompleteTextView(android.content.Context!);
-    ctor public AppCompatAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
@@ -472,9 +472,9 @@
   }
 
   public class AppCompatButton extends android.widget.Button implements androidx.core.widget.AutoSizeableTextView androidx.core.view.TintableBackgroundView {
-    ctor public AppCompatButton(android.content.Context!);
-    ctor public AppCompatButton(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatButton(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatButton(android.content.Context);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
@@ -487,9 +487,9 @@
   }
 
   public class AppCompatCheckBox extends android.widget.CheckBox implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton {
-    ctor public AppCompatCheckBox(android.content.Context!);
-    ctor public AppCompatCheckBox(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatCheckBox(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatCheckBox(android.content.Context);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
@@ -502,16 +502,16 @@
   }
 
   public class AppCompatCheckedTextView extends android.widget.CheckedTextView {
-    ctor public AppCompatCheckedTextView(android.content.Context!);
-    ctor public AppCompatCheckedTextView(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatCheckedTextView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatCheckedTextView(android.content.Context);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?, int);
     method public void setTextAppearance(android.content.Context!, int);
   }
 
   public class AppCompatEditText extends android.widget.EditText implements androidx.core.view.TintableBackgroundView {
-    ctor public AppCompatEditText(android.content.Context!);
-    ctor public AppCompatEditText(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatEditText(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatEditText(android.content.Context);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
@@ -521,9 +521,9 @@
   }
 
   public class AppCompatImageButton extends android.widget.ImageButton implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableImageSourceView {
-    ctor public AppCompatImageButton(android.content.Context!);
-    ctor public AppCompatImageButton(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatImageButton(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatImageButton(android.content.Context);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportImageTintList();
@@ -536,9 +536,9 @@
   }
 
   public class AppCompatImageView extends android.widget.ImageView implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableImageSourceView {
-    ctor public AppCompatImageView(android.content.Context!);
-    ctor public AppCompatImageView(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatImageView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatImageView(android.content.Context);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportImageTintList();
@@ -551,9 +551,9 @@
   }
 
   public class AppCompatMultiAutoCompleteTextView extends android.widget.MultiAutoCompleteTextView implements androidx.core.view.TintableBackgroundView {
-    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context!);
-    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
@@ -564,8 +564,8 @@
 
   public class AppCompatRadioButton extends android.widget.RadioButton implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton {
     ctor public AppCompatRadioButton(android.content.Context!);
-    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?);
+    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
@@ -578,24 +578,24 @@
   }
 
   public class AppCompatRatingBar extends android.widget.RatingBar {
-    ctor public AppCompatRatingBar(android.content.Context!);
-    ctor public AppCompatRatingBar(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatRatingBar(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatRatingBar(android.content.Context);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?, int);
   }
 
   public class AppCompatSeekBar extends android.widget.SeekBar {
-    ctor public AppCompatSeekBar(android.content.Context!);
-    ctor public AppCompatSeekBar(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatSeekBar(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatSeekBar(android.content.Context);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?, int);
   }
 
   public class AppCompatSpinner extends android.widget.Spinner implements androidx.core.view.TintableBackgroundView {
-    ctor public AppCompatSpinner(android.content.Context!);
-    ctor public AppCompatSpinner(android.content.Context!, int);
-    ctor public AppCompatSpinner(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatSpinner(android.content.Context!, android.util.AttributeSet!, int);
-    ctor public AppCompatSpinner(android.content.Context!, android.util.AttributeSet!, int, int);
-    ctor public AppCompatSpinner(android.content.Context!, android.util.AttributeSet!, int, int, android.content.res.Resources.Theme!);
+    ctor public AppCompatSpinner(android.content.Context);
+    ctor public AppCompatSpinner(android.content.Context, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int, android.content.res.Resources.Theme!);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
@@ -604,9 +604,9 @@
   }
 
   public class AppCompatTextView extends android.widget.TextView implements androidx.core.widget.AutoSizeableTextView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
-    ctor public AppCompatTextView(android.content.Context!);
-    ctor public AppCompatTextView(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatTextView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatTextView(android.content.Context);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
@@ -626,15 +626,15 @@
   }
 
   public class AppCompatToggleButton extends android.widget.ToggleButton {
-    ctor public AppCompatToggleButton(android.content.Context!);
-    ctor public AppCompatToggleButton(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatToggleButton(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatToggleButton(android.content.Context);
+    ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?, int);
   }
 
   public class LinearLayoutCompat extends android.view.ViewGroup {
-    ctor public LinearLayoutCompat(android.content.Context!);
-    ctor public LinearLayoutCompat(android.content.Context!, android.util.AttributeSet!);
-    ctor public LinearLayoutCompat(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public LinearLayoutCompat(android.content.Context);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?, int);
     method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateDefaultLayoutParams();
     method public androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
     method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
@@ -766,9 +766,9 @@
   }
 
   public class SearchView extends androidx.appcompat.widget.LinearLayoutCompat implements androidx.appcompat.view.CollapsibleActionView {
-    ctor public SearchView(android.content.Context!);
-    ctor public SearchView(android.content.Context!, android.util.AttributeSet!);
-    ctor public SearchView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public SearchView(android.content.Context);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet?);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet?, int);
     method public int getImeOptions();
     method public int getInputType();
     method public int getMaxWidth();
@@ -827,9 +827,9 @@
   }
 
   public class SwitchCompat extends android.widget.CompoundButton {
-    ctor public SwitchCompat(android.content.Context!);
-    ctor public SwitchCompat(android.content.Context!, android.util.AttributeSet!);
-    ctor public SwitchCompat(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public SwitchCompat(android.content.Context);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?, int);
     method public boolean getShowText();
     method public boolean getSplitTrack();
     method public int getSwitchMinWidth();
@@ -877,9 +877,9 @@
   }
 
   public class Toolbar extends android.view.ViewGroup {
-    ctor public Toolbar(android.content.Context!);
-    ctor public Toolbar(android.content.Context!, android.util.AttributeSet?);
-    ctor public Toolbar(android.content.Context!, android.util.AttributeSet?, int);
+    ctor public Toolbar(android.content.Context);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet?);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet?, int);
     method public void collapseActionView();
     method public void dismissPopupMenus();
     method protected androidx.appcompat.widget.Toolbar.LayoutParams! generateDefaultLayoutParams();
diff --git a/appcompat/api/current.txt b/appcompat/api/current.txt
index e0c9d81..22cb389 100644
--- a/appcompat/api/current.txt
+++ b/appcompat/api/current.txt
@@ -424,8 +424,8 @@
 package androidx.appcompat.widget {
 
   public class ActionMenuView extends androidx.appcompat.widget.LinearLayoutCompat {
-    ctor public ActionMenuView(android.content.Context!);
-    ctor public ActionMenuView(android.content.Context!, android.util.AttributeSet!);
+    ctor public ActionMenuView(android.content.Context);
+    ctor public ActionMenuView(android.content.Context, android.util.AttributeSet?);
     method public void dismissPopupMenus();
     method protected androidx.appcompat.widget.ActionMenuView.LayoutParams! generateDefaultLayoutParams();
     method public androidx.appcompat.widget.ActionMenuView.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
@@ -460,9 +460,9 @@
   }
 
   public class AppCompatAutoCompleteTextView extends android.widget.AutoCompleteTextView implements androidx.core.view.TintableBackgroundView {
-    ctor public AppCompatAutoCompleteTextView(android.content.Context!);
-    ctor public AppCompatAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
@@ -472,9 +472,9 @@
   }
 
   public class AppCompatButton extends android.widget.Button implements androidx.core.widget.AutoSizeableTextView androidx.core.view.TintableBackgroundView {
-    ctor public AppCompatButton(android.content.Context!);
-    ctor public AppCompatButton(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatButton(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatButton(android.content.Context);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatButton(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
@@ -487,9 +487,9 @@
   }
 
   public class AppCompatCheckBox extends android.widget.CheckBox implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton {
-    ctor public AppCompatCheckBox(android.content.Context!);
-    ctor public AppCompatCheckBox(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatCheckBox(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatCheckBox(android.content.Context);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatCheckBox(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
@@ -502,16 +502,16 @@
   }
 
   public class AppCompatCheckedTextView extends android.widget.CheckedTextView {
-    ctor public AppCompatCheckedTextView(android.content.Context!);
-    ctor public AppCompatCheckedTextView(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatCheckedTextView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatCheckedTextView(android.content.Context);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatCheckedTextView(android.content.Context, android.util.AttributeSet?, int);
     method public void setTextAppearance(android.content.Context!, int);
   }
 
   public class AppCompatEditText extends android.widget.EditText implements androidx.core.view.TintableBackgroundView {
-    ctor public AppCompatEditText(android.content.Context!);
-    ctor public AppCompatEditText(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatEditText(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatEditText(android.content.Context);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatEditText(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
@@ -521,9 +521,9 @@
   }
 
   public class AppCompatImageButton extends android.widget.ImageButton implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableImageSourceView {
-    ctor public AppCompatImageButton(android.content.Context!);
-    ctor public AppCompatImageButton(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatImageButton(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatImageButton(android.content.Context);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatImageButton(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportImageTintList();
@@ -536,9 +536,9 @@
   }
 
   public class AppCompatImageView extends android.widget.ImageView implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableImageSourceView {
-    ctor public AppCompatImageView(android.content.Context!);
-    ctor public AppCompatImageView(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatImageView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatImageView(android.content.Context);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatImageView(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportImageTintList();
@@ -551,9 +551,9 @@
   }
 
   public class AppCompatMultiAutoCompleteTextView extends android.widget.MultiAutoCompleteTextView implements androidx.core.view.TintableBackgroundView {
-    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context!);
-    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatMultiAutoCompleteTextView(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
@@ -564,8 +564,8 @@
 
   public class AppCompatRadioButton extends android.widget.RadioButton implements androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundButton {
     ctor public AppCompatRadioButton(android.content.Context!);
-    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?);
+    ctor public AppCompatRadioButton(android.content.Context!, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportButtonTintList();
@@ -578,24 +578,24 @@
   }
 
   public class AppCompatRatingBar extends android.widget.RatingBar {
-    ctor public AppCompatRatingBar(android.content.Context!);
-    ctor public AppCompatRatingBar(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatRatingBar(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatRatingBar(android.content.Context);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatRatingBar(android.content.Context, android.util.AttributeSet?, int);
   }
 
   public class AppCompatSeekBar extends android.widget.SeekBar {
-    ctor public AppCompatSeekBar(android.content.Context!);
-    ctor public AppCompatSeekBar(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatSeekBar(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatSeekBar(android.content.Context);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatSeekBar(android.content.Context, android.util.AttributeSet?, int);
   }
 
   public class AppCompatSpinner extends android.widget.Spinner implements androidx.core.view.TintableBackgroundView {
-    ctor public AppCompatSpinner(android.content.Context!);
-    ctor public AppCompatSpinner(android.content.Context!, int);
-    ctor public AppCompatSpinner(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatSpinner(android.content.Context!, android.util.AttributeSet!, int);
-    ctor public AppCompatSpinner(android.content.Context!, android.util.AttributeSet!, int, int);
-    ctor public AppCompatSpinner(android.content.Context!, android.util.AttributeSet!, int, int, android.content.res.Resources.Theme!);
+    ctor public AppCompatSpinner(android.content.Context);
+    ctor public AppCompatSpinner(android.content.Context, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int);
+    ctor public AppCompatSpinner(android.content.Context, android.util.AttributeSet?, int, int, android.content.res.Resources.Theme!);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method public void setBackgroundDrawable(android.graphics.drawable.Drawable!);
@@ -604,9 +604,9 @@
   }
 
   public class AppCompatTextView extends android.widget.TextView implements androidx.core.widget.AutoSizeableTextView androidx.core.view.TintableBackgroundView androidx.core.widget.TintableCompoundDrawablesView {
-    ctor public AppCompatTextView(android.content.Context!);
-    ctor public AppCompatTextView(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatTextView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatTextView(android.content.Context);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatTextView(android.content.Context, android.util.AttributeSet?, int);
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportBackgroundTintList();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
@@ -626,15 +626,15 @@
   }
 
   public class AppCompatToggleButton extends android.widget.ToggleButton {
-    ctor public AppCompatToggleButton(android.content.Context!);
-    ctor public AppCompatToggleButton(android.content.Context!, android.util.AttributeSet!);
-    ctor public AppCompatToggleButton(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public AppCompatToggleButton(android.content.Context);
+    ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?);
+    ctor public AppCompatToggleButton(android.content.Context, android.util.AttributeSet?, int);
   }
 
   public class LinearLayoutCompat extends android.view.ViewGroup {
-    ctor public LinearLayoutCompat(android.content.Context!);
-    ctor public LinearLayoutCompat(android.content.Context!, android.util.AttributeSet!);
-    ctor public LinearLayoutCompat(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public LinearLayoutCompat(android.content.Context);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?);
+    ctor public LinearLayoutCompat(android.content.Context, android.util.AttributeSet?, int);
     method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateDefaultLayoutParams();
     method public androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.util.AttributeSet!);
     method protected androidx.appcompat.widget.LinearLayoutCompat.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
@@ -766,9 +766,9 @@
   }
 
   public class SearchView extends androidx.appcompat.widget.LinearLayoutCompat implements androidx.appcompat.view.CollapsibleActionView {
-    ctor public SearchView(android.content.Context!);
-    ctor public SearchView(android.content.Context!, android.util.AttributeSet!);
-    ctor public SearchView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public SearchView(android.content.Context);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet?);
+    ctor public SearchView(android.content.Context, android.util.AttributeSet?, int);
     method public int getImeOptions();
     method public int getInputType();
     method public int getMaxWidth();
@@ -827,9 +827,9 @@
   }
 
   public class SwitchCompat extends android.widget.CompoundButton {
-    ctor public SwitchCompat(android.content.Context!);
-    ctor public SwitchCompat(android.content.Context!, android.util.AttributeSet!);
-    ctor public SwitchCompat(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public SwitchCompat(android.content.Context);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?);
+    ctor public SwitchCompat(android.content.Context, android.util.AttributeSet?, int);
     method public boolean getShowText();
     method public boolean getSplitTrack();
     method public int getSwitchMinWidth();
@@ -877,9 +877,9 @@
   }
 
   public class Toolbar extends android.view.ViewGroup {
-    ctor public Toolbar(android.content.Context!);
-    ctor public Toolbar(android.content.Context!, android.util.AttributeSet?);
-    ctor public Toolbar(android.content.Context!, android.util.AttributeSet?, int);
+    ctor public Toolbar(android.content.Context);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet?);
+    ctor public Toolbar(android.content.Context, android.util.AttributeSet?, int);
     method public void collapseActionView();
     method public void dismissPopupMenus();
     method protected androidx.appcompat.widget.Toolbar.LayoutParams! generateDefaultLayoutParams();
diff --git a/appcompat/benchmark/build.gradle b/appcompat/benchmark/build.gradle
index 21e4797..962cb98 100644
--- a/appcompat/benchmark/build.gradle
+++ b/appcompat/benchmark/build.gradle
@@ -21,11 +21,12 @@
     id("AndroidXPlugin")
     id("com.android.library")
     id("kotlin-android")
+    id("androidx.benchmark")
 }
 
 dependencies {
     androidTestImplementation(project(":appcompat"))
-    androidTestImplementation(project(":benchmark"))
+    androidTestImplementation(project(":benchmark:benchmark-junit4"))
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
diff --git a/appcompat/benchmark/src/androidTest/java/androidx/appcompat/benchmark/ViewInflationBenchmark.kt b/appcompat/benchmark/src/androidTest/java/androidx/appcompat/benchmark/ViewInflationBenchmark.kt
index 7976411..9817973 100644
--- a/appcompat/benchmark/src/androidTest/java/androidx/appcompat/benchmark/ViewInflationBenchmark.kt
+++ b/appcompat/benchmark/src/androidTest/java/androidx/appcompat/benchmark/ViewInflationBenchmark.kt
@@ -21,8 +21,8 @@
 import android.widget.FrameLayout
 import androidx.appcompat.app.AppCompatActivity
 import androidx.appcompat.benchmark.test.R
-import androidx.benchmark.BenchmarkRule
-import androidx.benchmark.measureRepeated
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.rule.ActivityTestRule
diff --git a/appcompat/build.gradle b/appcompat/build.gradle
index 5ce2ab6..e18e0bd9 100644
--- a/appcompat/build.gradle
+++ b/appcompat/build.gradle
@@ -12,7 +12,7 @@
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
 
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.cursoradapter:cursoradapter:1.0.0")
     api("androidx.fragment:fragment:1.1.0-rc01")
diff --git a/appcompat/lint-baseline.xml b/appcompat/lint-baseline.xml
index 9ac6bc0..222abda 100644
--- a/appcompat/lint-baseline.xml
+++ b/appcompat/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 3.5.0-beta04" client="gradle" variant="debug" version="3.5.0-beta04">
+<issues format="5" by="lint 3.5.0-beta05" client="gradle" variant="debug" version="3.5.0-beta05">
 
     <issue
         id="KotlinPropertyAccess"
@@ -23,7 +23,7 @@
         errorLine2="                   ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="194"
+            line="196"
             column="20"/>
     </issue>
 
@@ -1025,66 +1025,11 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActionBarContextView(Context context) {"
-        errorLine2="                                ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="57"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActionBarContextView(Context context, AttributeSet attrs) {"
-        errorLine2="                                ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="61"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActionBarContextView(Context context, AttributeSet attrs) {"
-        errorLine2="                                                 ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="61"
-            column="50"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) {"
-        errorLine2="                                ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="65"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) {"
-        errorLine2="                                                 ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="65"
-            column="50"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setCustomView(View view) {"
         errorLine2="                              ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="100"
+            line="101"
             column="31"/>
     </issue>
 
@@ -1095,7 +1040,7 @@
         errorLine2="                         ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="115"
+            line="116"
             column="26"/>
     </issue>
 
@@ -1106,7 +1051,7 @@
         errorLine2="                            ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="120"
+            line="121"
             column="29"/>
     </issue>
 
@@ -1117,7 +1062,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="125"
+            line="126"
             column="12"/>
     </issue>
 
@@ -1128,7 +1073,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="129"
+            line="130"
             column="12"/>
     </issue>
 
@@ -1139,7 +1084,7 @@
         errorLine2="                                  ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="160"
+            line="161"
             column="35"/>
     </issue>
 
@@ -1150,7 +1095,7 @@
         errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="230"
+            line="231"
             column="15"/>
     </issue>
 
@@ -1161,7 +1106,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="237"
+            line="238"
             column="12"/>
     </issue>
 
@@ -1172,7 +1117,7 @@
         errorLine2="                                                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="237"
+            line="238"
             column="56"/>
     </issue>
 
@@ -1183,7 +1128,7 @@
         errorLine2="                                               ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarContextView.java"
-            line="358"
+            line="359"
             column="48"/>
     </issue>
 
@@ -1366,44 +1311,11 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActionBarOverlayLayout(Context context) {"
-        errorLine2="                                  ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="138"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActionBarOverlayLayout(Context context, AttributeSet attrs) {"
-        errorLine2="                                  ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="142"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActionBarOverlayLayout(Context context, AttributeSet attrs) {"
-        errorLine2="                                                   ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="142"
-            column="52"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setActionBarVisibilityCallback(ActionBarVisibilityCallback cb) {"
         errorLine2="                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="168"
+            line="170"
             column="48"/>
     </issue>
 
@@ -1414,7 +1326,7 @@
         errorLine2="                                          ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="224"
+            line="226"
             column="43"/>
     </issue>
 
@@ -1425,7 +1337,7 @@
         errorLine2="                                       ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="288"
+            line="290"
             column="40"/>
     </issue>
 
@@ -1436,7 +1348,7 @@
         errorLine2="              ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="321"
+            line="323"
             column="15"/>
     </issue>
 
@@ -1447,7 +1359,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="326"
+            line="328"
             column="12"/>
     </issue>
 
@@ -1458,7 +1370,7 @@
         errorLine2="                                             ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="326"
+            line="328"
             column="46"/>
     </issue>
 
@@ -1469,7 +1381,7 @@
         errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="331"
+            line="333"
             column="15"/>
     </issue>
 
@@ -1480,7 +1392,7 @@
         errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="331"
+            line="333"
             column="59"/>
     </issue>
 
@@ -1491,7 +1403,7 @@
         errorLine2="                                        ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="336"
+            line="338"
             column="41"/>
     </issue>
 
@@ -1502,7 +1414,7 @@
         errorLine2="                     ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="452"
+            line="454"
             column="22"/>
     </issue>
 
@@ -1513,7 +1425,7 @@
         errorLine2="                               ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="472"
+            line="474"
             column="32"/>
     </issue>
 
@@ -1524,7 +1436,7 @@
         errorLine2="                                        ~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="473"
+            line="475"
             column="41"/>
     </issue>
 
@@ -1535,7 +1447,7 @@
         errorLine2="                                       ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="480"
+            line="482"
             column="40"/>
     </issue>
 
@@ -1546,7 +1458,7 @@
         errorLine2="                                                   ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="480"
+            line="482"
             column="52"/>
     </issue>
 
@@ -1557,7 +1469,7 @@
         errorLine2="                                       ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="485"
+            line="487"
             column="40"/>
     </issue>
 
@@ -1568,7 +1480,7 @@
         errorLine2="                                                   ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="485"
+            line="487"
             column="52"/>
     </issue>
 
@@ -1579,7 +1491,7 @@
         errorLine2="                                   ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="492"
+            line="494"
             column="36"/>
     </issue>
 
@@ -1590,7 +1502,7 @@
         errorLine2="                               ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="499"
+            line="501"
             column="32"/>
     </issue>
 
@@ -1601,7 +1513,7 @@
         errorLine2="                                  ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="507"
+            line="509"
             column="35"/>
     </issue>
 
@@ -1612,7 +1524,7 @@
         errorLine2="                                                               ~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="507"
+            line="509"
             column="64"/>
     </issue>
 
@@ -1623,7 +1535,7 @@
         errorLine2="                                       ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="516"
+            line="518"
             column="40"/>
     </issue>
 
@@ -1634,7 +1546,7 @@
         errorLine2="                                                   ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="516"
+            line="518"
             column="52"/>
     </issue>
 
@@ -1645,7 +1557,7 @@
         errorLine2="                                       ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="524"
+            line="526"
             column="40"/>
     </issue>
 
@@ -1656,7 +1568,7 @@
         errorLine2="                                                   ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="524"
+            line="526"
             column="52"/>
     </issue>
 
@@ -1667,7 +1579,7 @@
         errorLine2="                               ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="534"
+            line="536"
             column="32"/>
     </issue>
 
@@ -1678,7 +1590,7 @@
         errorLine2="                                   ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="541"
+            line="543"
             column="36"/>
     </issue>
 
@@ -1689,7 +1601,7 @@
         errorLine2="                                 ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="555"
+            line="557"
             column="34"/>
     </issue>
 
@@ -1700,7 +1612,7 @@
         errorLine2="                                  ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="569"
+            line="571"
             column="35"/>
     </issue>
 
@@ -1711,7 +1623,7 @@
         errorLine2="                                                               ~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="569"
+            line="571"
             column="64"/>
     </issue>
 
@@ -1722,7 +1634,7 @@
         errorLine2="                                    ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="574"
+            line="576"
             column="37"/>
     </issue>
 
@@ -1733,7 +1645,7 @@
         errorLine2="                                  ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="662"
+            line="664"
             column="35"/>
     </issue>
 
@@ -1744,7 +1656,7 @@
         errorLine2="                               ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="668"
+            line="670"
             column="32"/>
     </issue>
 
@@ -1755,7 +1667,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="674"
+            line="676"
             column="12"/>
     </issue>
 
@@ -1766,7 +1678,7 @@
         errorLine2="                        ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="719"
+            line="721"
             column="25"/>
     </issue>
 
@@ -1777,7 +1689,7 @@
         errorLine2="                        ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="767"
+            line="769"
             column="25"/>
     </issue>
 
@@ -1788,7 +1700,7 @@
         errorLine2="                                   ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="767"
+            line="769"
             column="36"/>
     </issue>
 
@@ -1799,7 +1711,7 @@
         errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="773"
+            line="775"
             column="43"/>
     </issue>
 
@@ -1810,7 +1722,7 @@
         errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="779"
+            line="781"
             column="46"/>
     </issue>
 
@@ -1821,7 +1733,7 @@
         errorLine2="                            ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="791"
+            line="793"
             column="29"/>
     </issue>
 
@@ -1832,7 +1744,7 @@
         errorLine2="                                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="791"
+            line="793"
             column="40"/>
     </issue>
 
@@ -1843,7 +1755,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="799"
+            line="801"
             column="29"/>
     </issue>
 
@@ -1854,7 +1766,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java"
-            line="803"
+            line="805"
             column="29"/>
     </issue>
 
@@ -2664,44 +2576,11 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActionMenuView(Context context) {"
-        errorLine2="                          ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="75"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActionMenuView(Context context, AttributeSet attrs) {"
-        errorLine2="                          ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="79"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActionMenuView(Context context, AttributeSet attrs) {"
-        errorLine2="                                           ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="79"
-            column="44"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setPresenter(ActionMenuPresenter presenter) {"
         errorLine2="                             ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="121"
+            line="122"
             column="30"/>
     </issue>
 
@@ -2712,7 +2591,7 @@
         errorLine2="                                       ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="127"
+            line="128"
             column="40"/>
     </issue>
 
@@ -2723,7 +2602,7 @@
         errorLine2="                                           ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="140"
+            line="141"
             column="44"/>
     </issue>
 
@@ -2734,7 +2613,7 @@
         errorLine2="              ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="582"
+            line="583"
             column="15"/>
     </issue>
 
@@ -2745,7 +2624,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="590"
+            line="591"
             column="12"/>
     </issue>
 
@@ -2756,7 +2635,7 @@
         errorLine2="                                             ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="590"
+            line="591"
             column="46"/>
     </issue>
 
@@ -2767,7 +2646,7 @@
         errorLine2="              ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="595"
+            line="596"
             column="15"/>
     </issue>
 
@@ -2778,7 +2657,7 @@
         errorLine2="                                                ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="595"
+            line="596"
             column="49"/>
     </issue>
 
@@ -2789,7 +2668,7 @@
         errorLine2="                                        ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="609"
+            line="610"
             column="41"/>
     </issue>
 
@@ -2800,7 +2679,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="615"
+            line="616"
             column="12"/>
     </issue>
 
@@ -2811,7 +2690,7 @@
         errorLine2="                              ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="624"
+            line="625"
             column="31"/>
     </issue>
 
@@ -2822,7 +2701,7 @@
         errorLine2="                           ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="638"
+            line="639"
             column="28"/>
     </issue>
 
@@ -2833,7 +2712,7 @@
         errorLine2="           ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="650"
+            line="651"
             column="12"/>
     </issue>
 
@@ -2844,7 +2723,7 @@
         errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="671"
+            line="672"
             column="34"/>
     </issue>
 
@@ -2855,7 +2734,7 @@
         errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="671"
+            line="672"
             column="62"/>
     </issue>
 
@@ -2866,7 +2745,7 @@
         errorLine2="           ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="681"
+            line="682"
             column="12"/>
     </issue>
 
@@ -2877,7 +2756,7 @@
         errorLine2="                                                      ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="749"
+            line="750"
             column="55"/>
     </issue>
 
@@ -2888,7 +2767,7 @@
         errorLine2="                                       ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="771"
+            line="772"
             column="40"/>
     </issue>
 
@@ -2899,7 +2778,7 @@
         errorLine2="                            ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="832"
+            line="833"
             column="29"/>
     </issue>
 
@@ -2910,7 +2789,7 @@
         errorLine2="                                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="832"
+            line="833"
             column="40"/>
     </issue>
 
@@ -2921,7 +2800,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="836"
+            line="837"
             column="29"/>
     </issue>
 
@@ -2932,7 +2811,7 @@
         errorLine2="                            ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActionMenuView.java"
-            line="840"
+            line="841"
             column="29"/>
     </issue>
 
@@ -3236,66 +3115,11 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActivityChooserView(Context context) {"
-        errorLine2="                               ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActivityChooserView.java"
-            line="200"
-            column="32"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActivityChooserView(Context context, AttributeSet attrs) {"
-        errorLine2="                               ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActivityChooserView.java"
-            line="210"
-            column="32"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActivityChooserView(Context context, AttributeSet attrs) {"
-        errorLine2="                                                ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActivityChooserView.java"
-            line="210"
-            column="49"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActivityChooserView(Context context, AttributeSet attrs, int defStyle) {"
-        errorLine2="                               ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActivityChooserView.java"
-            line="221"
-            column="32"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ActivityChooserView(Context context, AttributeSet attrs, int defStyle) {"
-        errorLine2="                                                ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ActivityChooserView.java"
-            line="221"
-            column="49"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setActivityChooserModel(ActivityChooserModel dataModel) {"
         errorLine2="                                        ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActivityChooserView.java"
-            line="303"
+            line="306"
             column="41"/>
     </issue>
 
@@ -3306,7 +3130,7 @@
         errorLine2="                                                        ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActivityChooserView.java"
-            line="322"
+            line="325"
             column="57"/>
     </issue>
 
@@ -3317,7 +3141,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActivityChooserView.java"
-            line="346"
+            line="349"
             column="29"/>
     </issue>
 
@@ -3328,7 +3152,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActivityChooserView.java"
-            line="483"
+            line="486"
             column="12"/>
     </issue>
 
@@ -3339,7 +3163,7 @@
         errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActivityChooserView.java"
-            line="492"
+            line="495"
             column="38"/>
     </issue>
 
@@ -3350,7 +3174,7 @@
         errorLine2="                           ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActivityChooserView.java"
-            line="873"
+            line="876"
             column="28"/>
     </issue>
 
@@ -3361,7 +3185,7 @@
         errorLine2="                                            ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ActivityChooserView.java"
-            line="873"
+            line="876"
             column="45"/>
     </issue>
 
@@ -5040,66 +4864,11 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatAutoCompleteTextView(Context context) {"
-        errorLine2="                                         ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java"
-            line="65"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatAutoCompleteTextView(Context context, AttributeSet attrs) {"
-        errorLine2="                                         ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java"
-            line="69"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatAutoCompleteTextView(Context context, AttributeSet attrs) {"
-        errorLine2="                                                          ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java"
-            line="69"
-            column="59"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                         ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java"
-            line="73"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                                          ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java"
-            line="73"
-            column="59"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setBackgroundDrawable(Drawable background) {"
         errorLine2="                                      ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java"
-            line="105"
+            line="107"
             column="39"/>
     </issue>
 
@@ -5110,7 +4879,7 @@
         errorLine2="                                  ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java"
-            line="180"
+            line="182"
             column="35"/>
     </issue>
 
@@ -5121,7 +4890,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java"
-            line="188"
+            line="190"
             column="12"/>
     </issue>
 
@@ -5132,7 +4901,7 @@
         errorLine2="                                                   ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java"
-            line="188"
+            line="190"
             column="52"/>
     </issue>
 
@@ -5143,73 +4912,18 @@
         errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java"
-            line="198"
+            line="200"
             column="54"/>
     </issue>
 
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatButton(Context context) {"
-        errorLine2="                           ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatButton.java"
-            line="63"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatButton(Context context, AttributeSet attrs) {"
-        errorLine2="                           ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatButton.java"
-            line="67"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatButton(Context context, AttributeSet attrs) {"
-        errorLine2="                                            ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatButton.java"
-            line="67"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatButton(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                           ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatButton.java"
-            line="71"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatButton(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                            ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatButton.java"
-            line="71"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setBackgroundDrawable(Drawable background) {"
         errorLine2="                                      ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatButton.java"
-            line="91"
+            line="92"
             column="39"/>
     </issue>
 
@@ -5220,7 +4934,7 @@
         errorLine2="                                  ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatButton.java"
-            line="166"
+            line="167"
             column="35"/>
     </issue>
 
@@ -5231,7 +4945,7 @@
         errorLine2="                                               ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatButton.java"
-            line="174"
+            line="175"
             column="48"/>
     </issue>
 
@@ -5242,7 +4956,7 @@
         errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatButton.java"
-            line="180"
+            line="181"
             column="51"/>
     </issue>
 
@@ -5253,7 +4967,7 @@
         errorLine2="                                 ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatButton.java"
-            line="205"
+            line="206"
             column="34"/>
     </issue>
 
@@ -5264,7 +4978,7 @@
         errorLine2="           ~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatButton.java"
-            line="339"
+            line="340"
             column="12"/>
     </issue>
 
@@ -5275,7 +4989,7 @@
         errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatButton.java"
-            line="370"
+            line="371"
             column="54"/>
     </issue>
 
@@ -5315,66 +5029,11 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatCheckBox(Context context) {"
-        errorLine2="                             ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatCheckBox.java"
-            line="60"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatCheckBox(Context context, AttributeSet attrs) {"
-        errorLine2="                             ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatCheckBox.java"
-            line="64"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatCheckBox(Context context, AttributeSet attrs) {"
-        errorLine2="                                              ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatCheckBox.java"
-            line="64"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                             ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatCheckBox.java"
-            line="68"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                              ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatCheckBox.java"
-            line="68"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setButtonDrawable(Drawable buttonDrawable) {"
         errorLine2="                                  ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatCheckBox.java"
-            line="81"
+            line="83"
             column="35"/>
     </issue>
 
@@ -5385,73 +5044,18 @@
         errorLine2="                                      ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatCheckBox.java"
-            line="207"
+            line="209"
             column="39"/>
     </issue>
 
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatCheckedTextView(Context context) {"
-        errorLine2="                                    ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatCheckedTextView.java"
-            line="46"
-            column="37"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatCheckedTextView(Context context, AttributeSet attrs) {"
-        errorLine2="                                    ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatCheckedTextView.java"
-            line="50"
-            column="37"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatCheckedTextView(Context context, AttributeSet attrs) {"
-        errorLine2="                                                     ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatCheckedTextView.java"
-            line="50"
-            column="54"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatCheckedTextView(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                    ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatCheckedTextView.java"
-            line="54"
-            column="37"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatCheckedTextView(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                                     ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatCheckedTextView.java"
-            line="54"
-            column="54"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setTextAppearance(Context context, int resId) {"
         errorLine2="                                  ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatCheckedTextView.java"
-            line="73"
+            line="76"
             column="35"/>
     </issue>
 
@@ -5462,7 +5066,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatCheckedTextView.java"
-            line="89"
+            line="92"
             column="12"/>
     </issue>
 
@@ -5473,7 +5077,7 @@
         errorLine2="                                                   ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatCheckedTextView.java"
-            line="89"
+            line="92"
             column="52"/>
     </issue>
 
@@ -5484,7 +5088,7 @@
         errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatCheckedTextView.java"
-            line="99"
+            line="102"
             column="54"/>
     </issue>
 
@@ -5865,66 +5469,11 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatEditText(Context context) {"
-        errorLine2="                             ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatEditText.java"
-            line="64"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatEditText(Context context, AttributeSet attrs) {"
-        errorLine2="                             ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatEditText.java"
-            line="68"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatEditText(Context context, AttributeSet attrs) {"
-        errorLine2="                                              ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatEditText.java"
-            line="68"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatEditText(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                             ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatEditText.java"
-            line="72"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatEditText(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                              ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatEditText.java"
-            line="72"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setBackgroundDrawable(Drawable background) {"
         errorLine2="                                      ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatEditText.java"
-            line="108"
+            line="109"
             column="39"/>
     </issue>
 
@@ -5935,7 +5484,7 @@
         errorLine2="                                  ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatEditText.java"
-            line="183"
+            line="184"
             column="35"/>
     </issue>
 
@@ -5946,7 +5495,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatEditText.java"
-            line="191"
+            line="192"
             column="12"/>
     </issue>
 
@@ -5957,7 +5506,7 @@
         errorLine2="                                                   ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatEditText.java"
-            line="191"
+            line="192"
             column="52"/>
     </issue>
 
@@ -5968,73 +5517,18 @@
         errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatEditText.java"
-            line="201"
+            line="202"
             column="54"/>
     </issue>
 
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatImageButton(Context context) {"
-        errorLine2="                                ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatImageButton.java"
-            line="64"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatImageButton(Context context, AttributeSet attrs) {"
-        errorLine2="                                ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatImageButton.java"
-            line="68"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatImageButton(Context context, AttributeSet attrs) {"
-        errorLine2="                                                 ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatImageButton.java"
-            line="68"
-            column="50"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatImageButton(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatImageButton.java"
-            line="72"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatImageButton(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                                 ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatImageButton.java"
-            line="72"
-            column="50"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setImageBitmap(Bitmap bm) {"
         errorLine2="                               ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatImageButton.java"
-            line="97"
+            line="99"
             column="32"/>
     </issue>
 
@@ -6045,95 +5539,29 @@
         errorLine2="                                      ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatImageButton.java"
-            line="121"
+            line="123"
             column="39"/>
     </issue>
 
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatImageHelper(ImageView view) {"
-        errorLine2="                                ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatImageHelper.java"
-            line="45"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {"
         errorLine2="                                   ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatImageHelper.java"
-            line="49"
+            line="50"
             column="36"/>
     </issue>
 
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatImageView(Context context) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatImageView.java"
-            line="63"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatImageView(Context context, AttributeSet attrs) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatImageView.java"
-            line="67"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatImageView(Context context, AttributeSet attrs) {"
-        errorLine2="                                               ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatImageView.java"
-            line="67"
-            column="48"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatImageView(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatImageView.java"
-            line="71"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatImageView(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                               ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatImageView.java"
-            line="71"
-            column="48"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setImageBitmap(Bitmap bm) {"
         errorLine2="                               ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatImageView.java"
-            line="107"
+            line="109"
             column="32"/>
     </issue>
 
@@ -6144,73 +5572,18 @@
         errorLine2="                                      ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatImageView.java"
-            line="131"
+            line="133"
             column="39"/>
     </issue>
 
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatMultiAutoCompleteTextView(Context context) {"
-        errorLine2="                                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView.java"
-            line="62"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatMultiAutoCompleteTextView(Context context, AttributeSet attrs) {"
-        errorLine2="                                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView.java"
-            line="66"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatMultiAutoCompleteTextView(Context context, AttributeSet attrs) {"
-        errorLine2="                                                               ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView.java"
-            line="66"
-            column="64"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatMultiAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView.java"
-            line="70"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatMultiAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                                               ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView.java"
-            line="70"
-            column="64"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setBackgroundDrawable(Drawable background) {"
         errorLine2="                                      ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView.java"
-            line="102"
+            line="105"
             column="39"/>
     </issue>
 
@@ -6221,7 +5594,7 @@
         errorLine2="                                  ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView.java"
-            line="177"
+            line="180"
             column="35"/>
     </issue>
 
@@ -6232,7 +5605,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView.java"
-            line="185"
+            line="188"
             column="12"/>
     </issue>
 
@@ -6243,7 +5616,7 @@
         errorLine2="                                                   ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView.java"
-            line="185"
+            line="188"
             column="52"/>
     </issue>
 
@@ -6261,7 +5634,7 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatRadioButton(Context context, AttributeSet attrs) {"
+        errorLine1="    public AppCompatRadioButton(Context context, @Nullable AttributeSet attrs) {"
         errorLine2="                                ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatRadioButton.java"
@@ -6272,18 +5645,7 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatRadioButton(Context context, AttributeSet attrs) {"
-        errorLine2="                                                 ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatRadioButton.java"
-            line="64"
-            column="50"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {"
+        errorLine1="    public AppCompatRadioButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {"
         errorLine2="                                ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatRadioButton.java"
@@ -6294,17 +5656,6 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                                 ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatRadioButton.java"
-            line="68"
-            column="50"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setButtonDrawable(Drawable buttonDrawable) {"
         errorLine2="                                  ~~~~~~~~">
         <location
@@ -6327,243 +5678,23 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatRatingBar(Context context) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatRatingBar.java"
-            line="39"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatRatingBar(Context context, AttributeSet attrs) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatRatingBar.java"
-            line="43"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatRatingBar(Context context, AttributeSet attrs) {"
-        errorLine2="                                               ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatRatingBar.java"
-            line="43"
-            column="48"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatRatingBar(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatRatingBar.java"
-            line="47"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatRatingBar(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                               ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatRatingBar.java"
-            line="47"
-            column="48"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSeekBar(Context context) {"
-        errorLine2="                            ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSeekBar.java"
-            line="38"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSeekBar(Context context, AttributeSet attrs) {"
-        errorLine2="                            ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSeekBar.java"
-            line="42"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSeekBar(Context context, AttributeSet attrs) {"
-        errorLine2="                                             ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSeekBar.java"
-            line="42"
-            column="46"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                            ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSeekBar.java"
-            line="46"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                             ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSeekBar.java"
-            line="46"
-            column="46"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    protected synchronized void onDraw(Canvas canvas) {"
         errorLine2="                                       ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatSeekBar.java"
-            line="54"
+            line="57"
             column="40"/>
     </issue>
 
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSpinner(Context context) {"
-        errorLine2="                            ~~~~~~~">
+        errorLine1="            int defStyleAttr, int mode, Resources.Theme popupTheme) {"
+        errorLine2="                                        ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="113"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSpinner(Context context, int mode) {"
-        errorLine2="                            ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="128"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSpinner(Context context, AttributeSet attrs) {"
-        errorLine2="                            ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="139"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSpinner(Context context, AttributeSet attrs) {"
-        errorLine2="                                             ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="139"
-            column="46"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                            ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="154"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                             ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="154"
-            column="46"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {"
-        errorLine2="                            ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="173"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {"
-        errorLine2="                                             ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="173"
-            column="46"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode,"
-        errorLine2="                            ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="201"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode,"
-        errorLine2="                                             ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="201"
-            column="46"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="            Resources.Theme popupTheme) {"
-        errorLine2="            ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="202"
-            column="13"/>
+            line="208"
+            column="41"/>
     </issue>
 
     <issue
@@ -6573,7 +5704,7 @@
         errorLine2="           ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="300"
+            line="306"
             column="12"/>
     </issue>
 
@@ -6584,7 +5715,7 @@
         errorLine2="                                           ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="305"
+            line="311"
             column="44"/>
     </issue>
 
@@ -6595,7 +5726,7 @@
         errorLine2="           ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="319"
+            line="325"
             column="12"/>
     </issue>
 
@@ -6606,7 +5737,7 @@
         errorLine2="                           ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="393"
+            line="399"
             column="28"/>
     </issue>
 
@@ -6617,7 +5748,7 @@
         errorLine2="                                ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="419"
+            line="425"
             column="33"/>
     </issue>
 
@@ -6628,7 +5759,7 @@
         errorLine2="                          ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="454"
+            line="460"
             column="27"/>
     </issue>
 
@@ -6639,7 +5770,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="463"
+            line="469"
             column="12"/>
     </issue>
 
@@ -6650,7 +5781,7 @@
         errorLine2="                                      ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="476"
+            line="482"
             column="39"/>
     </issue>
 
@@ -6661,7 +5792,7 @@
         errorLine2="           ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="608"
+            line="614"
             column="12"/>
     </issue>
 
@@ -6672,73 +5803,18 @@
         errorLine2="                                       ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatSpinner.java"
-            line="616"
+            line="622"
             column="40"/>
     </issue>
 
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatTextView(Context context) {"
-        errorLine2="                             ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatTextView.java"
-            line="90"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatTextView(Context context, AttributeSet attrs) {"
-        errorLine2="                             ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatTextView.java"
-            line="94"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatTextView(Context context, AttributeSet attrs) {"
-        errorLine2="                                              ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatTextView.java"
-            line="94"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatTextView(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                             ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatTextView.java"
-            line="98"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatTextView(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                              ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatTextView.java"
-            line="98"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setBackgroundDrawable(Drawable background) {"
         errorLine2="                                      ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatTextView.java"
-            line="120"
+            line="121"
             column="39"/>
     </issue>
 
@@ -6749,7 +5825,7 @@
         errorLine2="                                  ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatTextView.java"
-            line="184"
+            line="185"
             column="35"/>
     </issue>
 
@@ -6760,7 +5836,7 @@
         errorLine2="                                 ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatTextView.java"
-            line="222"
+            line="223"
             column="34"/>
     </issue>
 
@@ -6771,7 +5847,7 @@
         errorLine2="           ~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatTextView.java"
-            line="383"
+            line="384"
             column="12"/>
     </issue>
 
@@ -6782,7 +5858,7 @@
         errorLine2="           ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatTextView.java"
-            line="395"
+            line="396"
             column="12"/>
     </issue>
 
@@ -6793,7 +5869,7 @@
         errorLine2="                                                   ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatTextView.java"
-            line="395"
+            line="396"
             column="52"/>
     </issue>
 
@@ -6804,7 +5880,7 @@
         errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatTextView.java"
-            line="440"
+            line="441"
             column="54"/>
     </issue>
 
@@ -6815,68 +5891,13 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/AppCompatTextView.java"
-            line="494"
+            line="495"
             column="12"/>
     </issue>
 
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatToggleButton(Context context) {"
-        errorLine2="                                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatToggleButton.java"
-            line="37"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatToggleButton(Context context, AttributeSet attrs) {"
-        errorLine2="                                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatToggleButton.java"
-            line="41"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatToggleButton(Context context, AttributeSet attrs) {"
-        errorLine2="                                                  ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatToggleButton.java"
-            line="41"
-            column="51"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatToggleButton(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatToggleButton.java"
-            line="45"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public AppCompatToggleButton(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                                  ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/AppCompatToggleButton.java"
-            line="45"
-            column="51"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    protected AppCompatTextView createTextView(Context context, AttributeSet attrs) {"
         errorLine2="                                               ~~~~~~~">
         <location
@@ -7548,88 +6569,11 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ButtonBarLayout(Context context, AttributeSet attrs) {"
-        errorLine2="                           ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ButtonBarLayout.java"
-            line="50"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ButtonBarLayout(Context context, AttributeSet attrs) {"
-        errorLine2="                                            ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ButtonBarLayout.java"
-            line="50"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ContentFrameLayout(Context context) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ContentFrameLayout.java"
-            line="58"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ContentFrameLayout(Context context, AttributeSet attrs) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ContentFrameLayout.java"
-            line="62"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ContentFrameLayout(Context context, AttributeSet attrs) {"
-        errorLine2="                                               ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ContentFrameLayout.java"
-            line="62"
-            column="48"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ContentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ContentFrameLayout.java"
-            line="66"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ContentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                               ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ContentFrameLayout.java"
-            line="66"
-            column="48"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void dispatchFitSystemWindows(Rect insets) {"
         errorLine2="                                         ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ContentFrameLayout.java"
-            line="75"
+            line="78"
             column="42"/>
     </issue>
 
@@ -7640,7 +6584,7 @@
         errorLine2="                                  ~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ContentFrameLayout.java"
-            line="79"
+            line="82"
             column="35"/>
     </issue>
 
@@ -7651,7 +6595,7 @@
         errorLine2="           ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ContentFrameLayout.java"
-            line="174"
+            line="177"
             column="12"/>
     </issue>
 
@@ -7662,7 +6606,7 @@
         errorLine2="           ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ContentFrameLayout.java"
-            line="179"
+            line="182"
             column="12"/>
     </issue>
 
@@ -7673,7 +6617,7 @@
         errorLine2="           ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ContentFrameLayout.java"
-            line="184"
+            line="187"
             column="12"/>
     </issue>
 
@@ -7684,7 +6628,7 @@
         errorLine2="           ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ContentFrameLayout.java"
-            line="189"
+            line="192"
             column="12"/>
     </issue>
 
@@ -7695,7 +6639,7 @@
         errorLine2="           ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ContentFrameLayout.java"
-            line="194"
+            line="197"
             column="12"/>
     </issue>
 
@@ -7706,7 +6650,7 @@
         errorLine2="           ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ContentFrameLayout.java"
-            line="199"
+            line="202"
             column="12"/>
     </issue>
 
@@ -8219,61 +7163,6 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public DialogTitle(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                       ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/DialogTitle.java"
-            line="39"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public DialogTitle(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                        ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/DialogTitle.java"
-            line="39"
-            column="41"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public DialogTitle(Context context, AttributeSet attrs) {"
-        errorLine2="                       ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/DialogTitle.java"
-            line="43"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public DialogTitle(Context context, AttributeSet attrs) {"
-        errorLine2="                                        ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/DialogTitle.java"
-            line="43"
-            column="41"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public DialogTitle(Context context) {"
-        errorLine2="                       ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/DialogTitle.java"
-            line="47"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public DrawerArrowDrawable(Context context) {"
         errorLine2="                               ~~~~~~~">
         <location
@@ -8406,44 +7295,11 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public FitWindowsFrameLayout(Context context) {"
-        errorLine2="                                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/FitWindowsFrameLayout.java"
-            line="36"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public FitWindowsFrameLayout(Context context, AttributeSet attrs) {"
-        errorLine2="                                 ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/FitWindowsFrameLayout.java"
-            line="40"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public FitWindowsFrameLayout(Context context, AttributeSet attrs) {"
-        errorLine2="                                                  ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/FitWindowsFrameLayout.java"
-            line="40"
-            column="51"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setOnFitSystemWindowsListener(OnFitSystemWindowsListener listener) {"
         errorLine2="                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/FitWindowsFrameLayout.java"
-            line="45"
+            line="47"
             column="47"/>
     </issue>
 
@@ -8454,51 +7310,18 @@
         errorLine2="                                       ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/FitWindowsFrameLayout.java"
-            line="50"
+            line="52"
             column="40"/>
     </issue>
 
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public FitWindowsLinearLayout(Context context) {"
-        errorLine2="                                  ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/FitWindowsLinearLayout.java"
-            line="36"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public FitWindowsLinearLayout(Context context, AttributeSet attrs) {"
-        errorLine2="                                  ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/FitWindowsLinearLayout.java"
-            line="40"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public FitWindowsLinearLayout(Context context, AttributeSet attrs) {"
-        errorLine2="                                                   ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/FitWindowsLinearLayout.java"
-            line="40"
-            column="52"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setOnFitSystemWindowsListener(OnFitSystemWindowsListener listener) {"
         errorLine2="                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/FitWindowsLinearLayout.java"
-            line="45"
+            line="47"
             column="47"/>
     </issue>
 
@@ -8509,7 +7332,7 @@
         errorLine2="                                       ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/FitWindowsLinearLayout.java"
-            line="50"
+            line="52"
             column="40"/>
     </issue>
 
@@ -8604,66 +7427,11 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public LinearLayoutCompat(Context context) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="148"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public LinearLayoutCompat(Context context, AttributeSet attrs) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="152"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public LinearLayoutCompat(Context context, AttributeSet attrs) {"
-        errorLine2="                                               ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="152"
-            column="48"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public LinearLayoutCompat(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                              ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="156"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public LinearLayoutCompat(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                               ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="156"
-            column="48"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public Drawable getDividerDrawable() {"
         errorLine2="           ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="224"
+            line="233"
             column="12"/>
     </issue>
 
@@ -8674,7 +7442,7 @@
         errorLine2="                                   ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="235"
+            line="244"
             column="36"/>
     </issue>
 
@@ -8685,7 +7453,7 @@
         errorLine2="                          ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="286"
+            line="295"
             column="27"/>
     </issue>
 
@@ -8696,7 +7464,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="1724"
+            line="1733"
             column="12"/>
     </issue>
 
@@ -8707,7 +7475,7 @@
         errorLine2="                                             ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="1724"
+            line="1733"
             column="46"/>
     </issue>
 
@@ -8718,7 +7486,7 @@
         errorLine2="              ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="1737"
+            line="1746"
             column="15"/>
     </issue>
 
@@ -8729,7 +7497,7 @@
         errorLine2="              ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="1747"
+            line="1756"
             column="15"/>
     </issue>
 
@@ -8740,7 +7508,7 @@
         errorLine2="                                                ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="1747"
+            line="1756"
             column="49"/>
     </issue>
 
@@ -8751,7 +7519,7 @@
         errorLine2="                                        ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="1754"
+            line="1763"
             column="41"/>
     </issue>
 
@@ -8762,7 +7530,7 @@
         errorLine2="                                               ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="1759"
+            line="1768"
             column="48"/>
     </issue>
 
@@ -8773,7 +7541,7 @@
         errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="1765"
+            line="1774"
             column="51"/>
     </issue>
 
@@ -8784,7 +7552,7 @@
         errorLine2="                            ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="1792"
+            line="1801"
             column="29"/>
     </issue>
 
@@ -8795,7 +7563,7 @@
         errorLine2="                                       ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="1792"
+            line="1801"
             column="40"/>
     </issue>
 
@@ -8806,7 +7574,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="1829"
+            line="1838"
             column="29"/>
     </issue>
 
@@ -8817,7 +7585,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="1836"
+            line="1845"
             column="29"/>
     </issue>
 
@@ -8828,7 +7596,7 @@
         errorLine2="                            ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java"
-            line="1846"
+            line="1855"
             column="29"/>
     </issue>
 
@@ -11101,33 +9869,11 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public MenuPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {"
-        errorLine2="                           ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/MenuPopupWindow.java"
-            line="71"
-            column="28"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public MenuPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {"
-        errorLine2="                                            ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/MenuPopupWindow.java"
-            line="71"
-            column="45"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setEnterTransition(Object enterTransition) {"
         errorLine2="                                   ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/MenuPopupWindow.java"
-            line="82"
+            line="85"
             column="36"/>
     </issue>
 
@@ -11138,7 +9884,7 @@
         errorLine2="                                  ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/MenuPopupWindow.java"
-            line="88"
+            line="91"
             column="35"/>
     </issue>
 
@@ -11149,7 +9895,7 @@
         errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/MenuPopupWindow.java"
-            line="94"
+            line="97"
             column="34"/>
     </issue>
 
@@ -11160,7 +9906,7 @@
         errorLine2="                                    ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/MenuPopupWindow.java"
-            line="143"
+            line="146"
             column="37"/>
     </issue>
 
@@ -11171,7 +9917,7 @@
         errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/MenuPopupWindow.java"
-            line="158"
+            line="161"
             column="38"/>
     </issue>
 
@@ -11182,7 +9928,7 @@
         errorLine2="                                              ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/MenuPopupWindow.java"
-            line="167"
+            line="170"
             column="47"/>
     </issue>
 
@@ -11193,7 +9939,7 @@
         errorLine2="                                    ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/MenuPopupWindow.java"
-            line="188"
+            line="191"
             column="37"/>
     </issue>
 
@@ -11688,29 +10434,18 @@
         errorLine2="              ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java"
-            line="74"
+            line="75"
             column="15"/>
     </issue>
 
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ScrollingTabContainerView(Context context) {"
-        errorLine2="                                     ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java"
-            line="81"
-            column="38"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    protected void onConfigurationChanged(Configuration newConfig) {"
         errorLine2="                                          ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java"
-            line="224"
+            line="225"
             column="43"/>
     </issue>
 
@@ -11721,7 +10456,7 @@
         errorLine2="                       ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java"
-            line="309"
+            line="310"
             column="24"/>
     </issue>
 
@@ -11732,7 +10467,7 @@
         errorLine2="                       ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java"
-            line="324"
+            line="325"
             column="24"/>
     </issue>
 
@@ -11743,7 +10478,7 @@
         errorLine2="                               ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java"
-            line="370"
+            line="371"
             column="32"/>
     </issue>
 
@@ -11754,7 +10489,7 @@
         errorLine2="                                                           ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java"
-            line="370"
+            line="371"
             column="60"/>
     </issue>
 
@@ -11765,7 +10500,7 @@
         errorLine2="                                  ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java"
-            line="376"
+            line="377"
             column="35"/>
     </issue>
 
@@ -11776,7 +10511,7 @@
         errorLine2="                                  ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="216"
+            line="217"
             column="35"/>
     </issue>
 
@@ -11787,73 +10522,18 @@
         errorLine2="                                  ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="226"
+            line="227"
             column="35"/>
     </issue>
 
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public SearchView(Context context) {"
-        errorLine2="                      ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="266"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public SearchView(Context context, AttributeSet attrs) {"
-        errorLine2="                      ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="270"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public SearchView(Context context, AttributeSet attrs) {"
-        errorLine2="                                       ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="270"
-            column="40"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public SearchView(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                      ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="274"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public SearchView(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                       ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="274"
-            column="40"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setSearchableInfo(SearchableInfo searchable) {"
         errorLine2="                                  ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="405"
+            line="406"
             column="35"/>
     </issue>
 
@@ -11864,7 +10544,7 @@
         errorLine2="                                 ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="428"
+            line="429"
             column="34"/>
     </issue>
 
@@ -11875,7 +10555,7 @@
         errorLine2="                                               ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="478"
+            line="479"
             column="48"/>
     </issue>
 
@@ -11886,7 +10566,7 @@
         errorLine2="                                       ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="510"
+            line="511"
             column="40"/>
     </issue>
 
@@ -11897,7 +10577,7 @@
         errorLine2="                                   ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="519"
+            line="520"
             column="36"/>
     </issue>
 
@@ -11908,7 +10588,7 @@
         errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="528"
+            line="529"
             column="51"/>
     </issue>
 
@@ -11919,7 +10599,7 @@
         errorLine2="                                        ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="537"
+            line="538"
             column="41"/>
     </issue>
 
@@ -11930,7 +10610,7 @@
         errorLine2="                                         ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="549"
+            line="550"
             column="42"/>
     </issue>
 
@@ -11941,7 +10621,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="558"
+            line="559"
             column="12"/>
     </issue>
 
@@ -11952,7 +10632,7 @@
         errorLine2="                         ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="570"
+            line="571"
             column="26"/>
     </issue>
 
@@ -11963,7 +10643,7 @@
         errorLine2="                                      ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="744"
+            line="745"
             column="39"/>
     </issue>
 
@@ -11974,7 +10654,7 @@
         errorLine2="           ~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="754"
+            line="755"
             column="12"/>
     </issue>
 
@@ -11985,7 +10665,7 @@
         errorLine2="              ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="1344"
+            line="1345"
             column="15"/>
     </issue>
 
@@ -11996,7 +10676,7 @@
         errorLine2="                                          ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="1352"
+            line="1353"
             column="43"/>
     </issue>
 
@@ -12007,7 +10687,7 @@
         errorLine2="                                  ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="1841"
+            line="1842"
             column="35"/>
     </issue>
 
@@ -12018,7 +10698,7 @@
         errorLine2="                                  ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="1845"
+            line="1846"
             column="35"/>
     </issue>
 
@@ -12029,7 +10709,7 @@
         errorLine2="                                                   ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="1845"
+            line="1846"
             column="52"/>
     </issue>
 
@@ -12040,7 +10720,7 @@
         errorLine2="                                  ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="1849"
+            line="1850"
             column="35"/>
     </issue>
 
@@ -12051,7 +10731,7 @@
         errorLine2="                                                   ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="1849"
+            line="1850"
             column="52"/>
     </issue>
 
@@ -12062,7 +10742,7 @@
         errorLine2="                                   ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="1884"
+            line="1885"
             column="36"/>
     </issue>
 
@@ -12073,7 +10753,7 @@
         errorLine2="                                                                      ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="1920"
+            line="1921"
             column="71"/>
     </issue>
 
@@ -12084,7 +10764,7 @@
         errorLine2="                                                ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="1935"
+            line="1936"
             column="49"/>
     </issue>
 
@@ -12095,7 +10775,7 @@
         errorLine2="               ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="1982"
+            line="1983"
             column="16"/>
     </issue>
 
@@ -12106,7 +10786,7 @@
         errorLine2="                                                       ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SearchView.java"
-            line="1982"
+            line="1983"
             column="56"/>
     </issue>
 
@@ -12905,66 +11585,11 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public SwitchCompat(Context context) {"
-        errorLine2="                        ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="193"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public SwitchCompat(Context context, AttributeSet attrs) {"
-        errorLine2="                        ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="204"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public SwitchCompat(Context context, AttributeSet attrs) {"
-        errorLine2="                                         ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="204"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public SwitchCompat(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                        ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="218"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public SwitchCompat(Context context, AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                                         ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="218"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setSwitchTextAppearance(Context context, int resid) {"
         errorLine2="                                        ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="303"
+            line="310"
             column="41"/>
     </issue>
 
@@ -12975,7 +11600,7 @@
         errorLine2="                                  ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="367"
+            line="374"
             column="35"/>
     </issue>
 
@@ -12986,7 +11611,7 @@
         errorLine2="                                  ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="395"
+            line="402"
             column="35"/>
     </issue>
 
@@ -12997,7 +11622,7 @@
         errorLine2="                                 ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="483"
+            line="490"
             column="34"/>
     </issue>
 
@@ -13008,7 +11633,7 @@
         errorLine2="           ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="512"
+            line="519"
             column="12"/>
     </issue>
 
@@ -13019,7 +11644,7 @@
         errorLine2="                                 ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="602"
+            line="609"
             column="34"/>
     </issue>
 
@@ -13030,7 +11655,7 @@
         errorLine2="           ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="633"
+            line="640"
             column="12"/>
     </issue>
 
@@ -13041,7 +11666,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="745"
+            line="752"
             column="12"/>
     </issue>
 
@@ -13052,7 +11677,7 @@
         errorLine2="                          ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="754"
+            line="761"
             column="27"/>
     </issue>
 
@@ -13063,7 +11688,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="764"
+            line="771"
             column="12"/>
     </issue>
 
@@ -13074,7 +11699,7 @@
         errorLine2="                           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="773"
+            line="780"
             column="28"/>
     </issue>
 
@@ -13085,7 +11710,7 @@
         errorLine2="                                             ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="868"
+            line="875"
             column="46"/>
     </issue>
 
@@ -13096,7 +11721,7 @@
         errorLine2="                                ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="909"
+            line="916"
             column="33"/>
     </issue>
 
@@ -13107,7 +11732,7 @@
         errorLine2="                     ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="1136"
+            line="1143"
             column="22"/>
     </issue>
 
@@ -13118,7 +11743,7 @@
         errorLine2="                          ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="1201"
+            line="1208"
             column="27"/>
     </issue>
 
@@ -13129,7 +11754,7 @@
         errorLine2="              ~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="1325"
+            line="1332"
             column="15"/>
     </issue>
 
@@ -13140,7 +11765,7 @@
         errorLine2="                                     ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="1371"
+            line="1378"
             column="38"/>
     </issue>
 
@@ -13151,7 +11776,7 @@
         errorLine2="                                               ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="1394"
+            line="1401"
             column="48"/>
     </issue>
 
@@ -13162,7 +11787,7 @@
         errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="1400"
+            line="1407"
             column="51"/>
     </issue>
 
@@ -13173,7 +11798,7 @@
         errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/SwitchCompat.java"
-            line="1421"
+            line="1428"
             column="54"/>
     </issue>
 
@@ -13301,11 +11926,22 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
+        errorLine1="    public TypedArray getWrappedTypeArray() {"
+        errorLine2="           ~~~~~~~~~~">
+        <location
+            file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"
+            line="75"
+            column="12"/>
+    </issue>
+
+    <issue
+        id="UnknownNullness"
+        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public Drawable getDrawable(int index) {"
         errorLine2="           ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"
-            line="72"
+            line="79"
             column="12"/>
     </issue>
 
@@ -13316,7 +11952,7 @@
         errorLine2="           ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"
-            line="82"
+            line="89"
             column="12"/>
     </issue>
 
@@ -13327,7 +11963,7 @@
         errorLine2="           ~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"
-            line="134"
+            line="141"
             column="12"/>
     </issue>
 
@@ -13338,7 +11974,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"
-            line="138"
+            line="145"
             column="12"/>
     </issue>
 
@@ -13349,7 +11985,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"
-            line="142"
+            line="149"
             column="12"/>
     </issue>
 
@@ -13360,7 +11996,7 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"
-            line="146"
+            line="153"
             column="12"/>
     </issue>
 
@@ -13371,7 +12007,7 @@
         errorLine2="           ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"
-            line="166"
+            line="173"
             column="12"/>
     </issue>
 
@@ -13382,7 +12018,7 @@
         errorLine2="                                             ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"
-            line="196"
+            line="203"
             column="46"/>
     </issue>
 
@@ -13393,7 +12029,7 @@
         errorLine2="           ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"
-            line="212"
+            line="219"
             column="12"/>
     </issue>
 
@@ -13404,7 +12040,7 @@
         errorLine2="                                       ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"
-            line="216"
+            line="223"
             column="40"/>
     </issue>
 
@@ -13415,7 +12051,7 @@
         errorLine2="           ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"
-            line="236"
+            line="243"
             column="12"/>
     </issue>
 
@@ -13426,51 +12062,18 @@
         errorLine2="           ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/TintTypedArray.java"
-            line="240"
+            line="247"
             column="12"/>
     </issue>
 
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public Toolbar(Context context) {"
-        errorLine2="                   ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="227"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public Toolbar(Context context, @Nullable AttributeSet attrs) {"
-        errorLine2="                   ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="231"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public Toolbar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {"
-        errorLine2="                   ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="235"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setMenu(MenuBuilder menu, ActionMenuPresenter outerPresenter) {"
         errorLine2="                        ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="554"
+            line="559"
             column="25"/>
     </issue>
 
@@ -13481,7 +12084,7 @@
         errorLine2="                                          ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="554"
+            line="559"
             column="43"/>
     </issue>
 
@@ -13492,7 +12095,7 @@
         errorLine2="                        ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="628"
+            line="633"
             column="25"/>
     </issue>
 
@@ -13503,7 +12106,7 @@
         errorLine2="           ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="650"
+            line="655"
             column="12"/>
     </issue>
 
@@ -13514,7 +12117,7 @@
         errorLine2="                                   ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="674"
+            line="679"
             column="36"/>
     </issue>
 
@@ -13525,7 +12128,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="688"
+            line="693"
             column="12"/>
     </issue>
 
@@ -13536,7 +12139,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="735"
+            line="740"
             column="12"/>
     </issue>
 
@@ -13547,7 +12150,7 @@
         errorLine2="                         ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="759"
+            line="764"
             column="26"/>
     </issue>
 
@@ -13558,7 +12161,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="791"
+            line="796"
             column="12"/>
     </issue>
 
@@ -13569,7 +12172,7 @@
         errorLine2="                            ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="813"
+            line="818"
             column="29"/>
     </issue>
 
@@ -13580,7 +12183,7 @@
         errorLine2="                                       ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="844"
+            line="849"
             column="40"/>
     </issue>
 
@@ -13591,7 +12194,7 @@
         errorLine2="                                          ~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="855"
+            line="860"
             column="43"/>
     </issue>
 
@@ -13602,7 +12205,7 @@
         errorLine2="                                             ~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="1019"
+            line="1024"
             column="46"/>
     </issue>
 
@@ -13613,7 +12216,7 @@
         errorLine2="           ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="1125"
+            line="1130"
             column="12"/>
     </issue>
 
@@ -13624,7 +12227,7 @@
         errorLine2="                                           ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="1202"
+            line="1207"
             column="44"/>
     </issue>
 
@@ -13635,7 +12238,7 @@
         errorLine2="              ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="1530"
+            line="1535"
             column="15"/>
     </issue>
 
@@ -13646,7 +12249,7 @@
         errorLine2="                                          ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="1542"
+            line="1547"
             column="43"/>
     </issue>
 
@@ -13657,7 +12260,7 @@
         errorLine2="                                ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="1576"
+            line="1581"
             column="33"/>
     </issue>
 
@@ -13668,7 +12271,7 @@
         errorLine2="                                ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="1602"
+            line="1607"
             column="33"/>
     </issue>
 
@@ -13679,7 +12282,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2197"
+            line="2202"
             column="12"/>
     </issue>
 
@@ -13690,7 +12293,7 @@
         errorLine2="                                             ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2197"
+            line="2202"
             column="46"/>
     </issue>
 
@@ -13701,7 +12304,7 @@
         errorLine2="              ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2202"
+            line="2207"
             column="15"/>
     </issue>
 
@@ -13712,7 +12315,7 @@
         errorLine2="                                                ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2202"
+            line="2207"
             column="49"/>
     </issue>
 
@@ -13723,7 +12326,7 @@
         errorLine2="              ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2215"
+            line="2220"
             column="15"/>
     </issue>
 
@@ -13734,7 +12337,7 @@
         errorLine2="                                        ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2220"
+            line="2225"
             column="41"/>
     </issue>
 
@@ -13745,7 +12348,7 @@
         errorLine2="           ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2230"
+            line="2235"
             column="12"/>
     </issue>
 
@@ -13756,7 +12359,7 @@
         errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2279"
+            line="2284"
             column="34"/>
     </issue>
 
@@ -13767,7 +12370,7 @@
         errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2279"
+            line="2284"
             column="62"/>
     </issue>
 
@@ -13778,7 +12381,7 @@
         errorLine2="                                       ~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2334"
+            line="2339"
             column="40"/>
     </issue>
 
@@ -13789,7 +12392,7 @@
         errorLine2="                                                ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2353"
+            line="2358"
             column="49"/>
     </issue>
 
@@ -13800,7 +12403,7 @@
         errorLine2="                            ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2371"
+            line="2376"
             column="29"/>
     </issue>
 
@@ -13811,7 +12414,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2377"
+            line="2382"
             column="29"/>
     </issue>
 
@@ -13822,7 +12425,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2381"
+            line="2386"
             column="29"/>
     </issue>
 
@@ -13833,7 +12436,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2388"
+            line="2393"
             column="29"/>
     </issue>
 
@@ -13844,7 +12447,7 @@
         errorLine2="                          ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2404"
+            line="2409"
             column="27"/>
     </issue>
 
@@ -13855,7 +12458,7 @@
         errorLine2="                          ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2408"
+            line="2413"
             column="27"/>
     </issue>
 
@@ -13866,7 +12469,7 @@
         errorLine2="                                         ~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2408"
+            line="2413"
             column="42"/>
     </issue>
 
@@ -13877,7 +12480,7 @@
         errorLine2="                          ~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2414"
+            line="2419"
             column="27"/>
     </issue>
 
@@ -13888,7 +12491,7 @@
         errorLine2="                                  ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/Toolbar.java"
-            line="2419"
+            line="2424"
             column="35"/>
     </issue>
 
@@ -14324,55 +12927,11 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ViewStubCompat(Context context, AttributeSet attrs) {"
-        errorLine2="                          ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ViewStubCompat.java"
-            line="52"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ViewStubCompat(Context context, AttributeSet attrs) {"
-        errorLine2="                                           ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ViewStubCompat.java"
-            line="52"
-            column="44"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ViewStubCompat(Context context, AttributeSet attrs, int defStyle) {"
-        errorLine2="                          ~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ViewStubCompat.java"
-            line="56"
-            column="27"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
-        errorLine1="    public ViewStubCompat(Context context, AttributeSet attrs, int defStyle) {"
-        errorLine2="                                           ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/appcompat/widget/ViewStubCompat.java"
-            line="56"
-            column="44"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
         errorLine1="    public void setLayoutInflater(LayoutInflater inflater) {"
         errorLine2="                                  ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ViewStubCompat.java"
-            line="136"
+            line="138"
             column="35"/>
     </issue>
 
@@ -14383,7 +12942,7 @@
         errorLine2="           ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ViewStubCompat.java"
-            line="143"
+            line="145"
             column="12"/>
     </issue>
 
@@ -14394,7 +12953,7 @@
         errorLine2="                     ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ViewStubCompat.java"
-            line="154"
+            line="156"
             column="22"/>
     </issue>
 
@@ -14405,7 +12964,7 @@
         errorLine2="                                ~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ViewStubCompat.java"
-            line="158"
+            line="160"
             column="33"/>
     </issue>
 
@@ -14416,7 +12975,7 @@
         errorLine2="           ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ViewStubCompat.java"
-            line="195"
+            line="197"
             column="12"/>
     </issue>
 
@@ -14427,7 +12986,7 @@
         errorLine2="                                     ~~~~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ViewStubCompat.java"
-            line="247"
+            line="249"
             column="38"/>
     </issue>
 
@@ -14438,7 +12997,7 @@
         errorLine2="                       ~~~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ViewStubCompat.java"
-            line="266"
+            line="268"
             column="24"/>
     </issue>
 
@@ -14449,7 +13008,7 @@
         errorLine2="                                            ~~~~">
         <location
             file="src/main/java/androidx/appcompat/widget/ViewStubCompat.java"
-            line="266"
+            line="268"
             column="45"/>
     </issue>
 
diff --git a/appcompat/resources/api/1.2.0-alpha01.ignore b/appcompat/resources/api/1.2.0-alpha01.ignore
deleted file mode 100644
index 31e100e..0000000
--- a/appcompat/resources/api/1.2.0-alpha01.ignore
+++ /dev/null
@@ -1,3 +0,0 @@
-// Baseline format: 1.0
-HiddenSuperclass: androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat:
-    Public class androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat stripped of unavailable superclass androidx.appcompat.graphics.drawable.StateListDrawable
diff --git a/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatAttributeTest.kt b/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatAttributeTest.kt
new file mode 100644
index 0000000..8ec0a32
--- /dev/null
+++ b/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatAttributeTest.kt
@@ -0,0 +1,166 @@
+/*
+ * 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.appcompat.widget
+
+import android.view.ViewGroup
+import android.widget.CheckBox
+import android.widget.ImageView
+import android.widget.SeekBar
+import android.widget.TextView
+import androidx.appcompat.test.R
+import androidx.appcompat.app.AppCompatActivity
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.rule.ActivityTestRule
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+@SdkSuppress(minSdkVersion = 29)
+class AppCompatAttributeTest {
+    @get:Rule
+    val activityRule = ActivityTestRule(AppCompatActivity::class.java, true, false)
+
+    @Before
+    fun setup() {
+        InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand(
+            "settings put global debug_view_attributes_application_package " +
+                    "androidx.appcompat.test"
+        )
+        activityRule.launchActivity(null)
+    }
+
+    @After
+    fun tearDown() {
+        InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand(
+            "settings delete global debug_view_attributes_application_package"
+        )
+    }
+
+    @Test
+    fun testAppCompatImageViewAttributes() {
+        val root = activityRule.activity.layoutInflater.inflate(
+            R.layout.view_attribute_layout,
+            null
+        ) as ViewGroup
+        val imageView = root.findViewById<ImageView>(R.id.image_view)
+        assertTrue(imageView.attributeSourceResourceMap.isNotEmpty())
+        assertEquals(
+            R.layout.view_attribute_layout,
+            imageView.attributeSourceResourceMap[R.attr.srcCompat]
+        )
+        assertEquals(
+            R.layout.view_attribute_layout,
+            imageView.attributeSourceResourceMap[R.attr.backgroundTint]
+        )
+        assertEquals(
+            R.layout.view_attribute_layout,
+            imageView.attributeSourceResourceMap[R.attr.backgroundTintMode]
+        )
+    }
+
+    @Test
+    fun testAppCompatCheckBoxAttributes() {
+        val root = activityRule.activity.layoutInflater.inflate(
+            R.layout.view_attribute_layout,
+            null
+        ) as ViewGroup
+        val checkBox = root.findViewById<CheckBox>(R.id.check_box)
+        assertTrue(checkBox.attributeSourceResourceMap.isNotEmpty())
+        assertEquals(
+            R.layout.view_attribute_layout,
+            checkBox.attributeSourceResourceMap[R.attr.buttonTint]
+        )
+    }
+
+    @Test
+    fun testAppCompatSeekBarAttributes() {
+        val root = activityRule.activity.layoutInflater.inflate(
+            R.layout.view_attribute_layout,
+            null
+        ) as ViewGroup
+        val seekBar = root.findViewById<SeekBar>(R.id.seek_bar)
+        assertTrue(seekBar.attributeSourceResourceMap.isNotEmpty())
+        assertEquals(
+            R.layout.view_attribute_layout,
+            seekBar.attributeSourceResourceMap[R.attr.tickMarkTint]
+        )
+    }
+
+    @Test
+    fun testAppCompatTextViewAttributes() {
+        val root = activityRule.activity.layoutInflater.inflate(
+            R.layout.view_attribute_layout,
+            null
+        ) as ViewGroup
+        val textView = root.findViewById<TextView>(R.id.text_view)
+        assertTrue(textView.attributeSourceResourceMap.isNotEmpty())
+        assertEquals(
+            R.layout.view_attribute_layout,
+            textView.attributeSourceResourceMap[R.attr.autoSizeTextType]
+        )
+    }
+
+    @Test
+    fun testSwitchCompatAttributes() {
+        val root = activityRule.activity.layoutInflater.inflate(
+            R.layout.view_attribute_layout,
+            null
+        ) as ViewGroup
+        val switchCompat = root.findViewById<SwitchCompat>(R.id.switch_compat)
+        assertTrue(switchCompat.attributeSourceResourceMap.isNotEmpty())
+        assertEquals(
+            R.layout.view_attribute_layout,
+            switchCompat.attributeSourceResourceMap[R.attr.thumbTint]
+        )
+    }
+
+    @Test
+    fun testToolbarAttributes() {
+        val root = activityRule.activity.layoutInflater.inflate(
+            R.layout.view_attribute_layout,
+            null
+        ) as ViewGroup
+        val toolbar = root.findViewById<Toolbar>(R.id.toolbar)
+        assertTrue(toolbar.attributeSourceResourceMap.isNotEmpty())
+        assertEquals(
+            R.layout.view_attribute_layout,
+            toolbar.attributeSourceResourceMap[R.attr.titleMargin]
+        )
+    }
+
+    @Test
+    fun testLinearLayoutCompatAttributes() {
+        val root = activityRule.activity.layoutInflater.inflate(
+            R.layout.view_attribute_layout,
+            null
+        ) as LinearLayoutCompat
+        assertTrue(root.attributeSourceResourceMap.isNotEmpty())
+        assertEquals(
+            R.layout.view_attribute_layout,
+            root.attributeSourceResourceMap[R.attr.showDividers]
+        )
+    }
+}
\ No newline at end of file
diff --git a/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseImageViewTest.java b/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseImageViewTest.java
index 78aa513..fe4058d 100755
--- a/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseImageViewTest.java
+++ b/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseImageViewTest.java
@@ -35,7 +35,7 @@
 import androidx.appcompat.testutils.TestUtils;
 import androidx.core.content.res.ResourcesCompat;
 import androidx.core.graphics.ColorUtils;
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.MediumTest;
 
 import org.junit.Test;
 
@@ -65,7 +65,7 @@
      * image source tint lists on the same image source.
      */
     @Test
-    @SmallTest
+    @MediumTest
     public void testImageTintingAcrossStateChange() {
         final @IdRes int viewId = R.id.view_tinted_source;
         final Resources res = mActivity.getResources();
@@ -143,7 +143,7 @@
      * image source tinting mode.
      */
     @Test
-    @SmallTest
+    @MediumTest
     public void testImageTintingAcrossModeChange() {
         final @IdRes int viewId = R.id.view_untinted_source;
         final Resources res = mActivity.getResources();
@@ -214,7 +214,7 @@
      * Tests for behavior around setting a tint list without setting a tint mode
      */
     @Test
-    @SmallTest
+    @MediumTest
     public void testImageTintingWithDefaultMode() {
         final @IdRes int viewId = R.id.view_untinted_source;
         final Resources res = mActivity.getResources();
@@ -254,7 +254,7 @@
      * is applied correctly after changing the image source itself.
      */
     @Test
-    @SmallTest
+    @MediumTest
     public void testImageOpaqueTintingAcrossImageChange() {
         final @IdRes int viewId = R.id.view_tinted_no_source;
         final Resources res = mActivity.getResources();
@@ -309,7 +309,7 @@
      * is applied correctly after changing the image source itself.
      */
     @Test
-    @SmallTest
+    @MediumTest
     public void testImageTranslucentTintingAcrossImageChange() {
         final @IdRes int viewId = R.id.view_untinted_no_source;
         final Resources res = mActivity.getResources();
@@ -399,7 +399,7 @@
      * affect the tinting of the image source.
      */
     @Test
-    @SmallTest
+    @MediumTest
     public void testImageTintingAcrossBackgroundTintingChange() {
         final @IdRes int viewId = R.id.view_untinted_source;
         final Resources res = mActivity.getResources();
diff --git a/appcompat/src/androidTest/res/layout/view_attribute_layout.xml b/appcompat/src/androidTest/res/layout/view_attribute_layout.xml
new file mode 100644
index 0000000..fb7859f
--- /dev/null
+++ b/appcompat/src/androidTest/res/layout/view_attribute_layout.xml
@@ -0,0 +1,58 @@
+<?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.
+  -->
+
+<androidx.appcompat.widget.LinearLayoutCompat
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    app:showDividers="none">
+    <ImageView
+        android:id="@+id/image_view"
+        android:layout_width="1dp"
+        android:layout_height="1dp"
+        app:srcCompat="@drawable/avd_heart_empty"
+        app:backgroundTint="#ff00ff"
+        app:backgroundTintMode="add" />
+    <CheckBox
+        android:id="@+id/check_box"
+        android:layout_width="1dp"
+        android:layout_height="1dp"
+        app:buttonTint="#00ff00"/>
+    <SeekBar
+        android:id="@+id/seek_bar"
+        android:layout_width="1dp"
+        android:layout_height="1dp"
+        app:tickMarkTint="#ff0000"/>
+    <TextView
+        android:id="@+id/text_view"
+        android:layout_width="1dp"
+        android:layout_height="1dp"
+        app:autoSizeTextType="none" />
+
+    <androidx.appcompat.widget.SwitchCompat
+        android:id="@+id/switch_compat"
+        android:layout_width="1dp"
+        android:layout_height="1dp"
+        app:thumbTint="#0000ff" />
+    <androidx.appcompat.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="1dp"
+        android:layout_height="1dp"
+        app:titleMargin="1dp" />
+</androidx.appcompat.widget.LinearLayoutCompat>
\ No newline at end of file
diff --git a/appcompat/src/main/java/androidx/appcompat/app/AlertController.java b/appcompat/src/main/java/androidx/appcompat/app/AlertController.java
index cfb1f1a..9a24db5 100644
--- a/appcompat/src/main/java/androidx/appcompat/app/AlertController.java
+++ b/appcompat/src/main/java/androidx/appcompat/app/AlertController.java
@@ -797,9 +797,9 @@
             mButtonNeutral.setVisibility(View.GONE);
         } else {
             mButtonNeutral.setText(mButtonNeutralText);
-            if (mButtonPositiveIcon != null) {
-                mButtonPositiveIcon.setBounds(0, 0, mButtonIconDimen, mButtonIconDimen);
-                mButtonPositive.setCompoundDrawables(mButtonPositiveIcon, null, null, null);
+            if (mButtonNeutralIcon != null) {
+                mButtonNeutralIcon.setBounds(0, 0, mButtonIconDimen, mButtonIconDimen);
+                mButtonNeutral.setCompoundDrawables(mButtonNeutralIcon, null, null, null);
             }
             mButtonNeutral.setVisibility(View.VISIBLE);
             whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
diff --git a/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java b/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
index 1bfe3a3..fd10f53 100644
--- a/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
+++ b/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
@@ -2286,10 +2286,13 @@
             } catch (IllegalStateException e) {
                 // applyOverrideConfiguration throws an IllegalStateException if its resources
                 // have already been created. Since there's no way to check this beforehand we
-                // just have to try it and catch the exception.
-                Log.e(TAG, "updateForNightMode. Calling applyOverrideConfiguration() failed"
-                        + " with an exception. Will fall back to using"
-                        + " Resources.updateConfiguration()", e);
+                // just have to try it and catch the exception. We only log if we're actually
+                // trying to apply a uiMode configuration though.
+                if (newNightMode != applicationNightMode) {
+                    Log.e(TAG, "updateForNightMode. Calling applyOverrideConfiguration() failed"
+                            + " with an exception. Will fall back to using"
+                            + " Resources.updateConfiguration()", e);
+                }
                 handled = false;
             }
         }
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AbsActionBarView.java b/appcompat/src/main/java/androidx/appcompat/widget/AbsActionBarView.java
index 5949668..3ed4a46 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AbsActionBarView.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AbsActionBarView.java
@@ -26,6 +26,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.appcompat.R;
 import androidx.core.view.ViewCompat;
 import androidx.core.view.ViewPropertyAnimatorCompat;
@@ -48,15 +50,15 @@
     private boolean mEatingTouch;
     private boolean mEatingHover;
 
-    AbsActionBarView(Context context) {
+    AbsActionBarView(@NonNull Context context) {
         this(context, null);
     }
 
-    AbsActionBarView(Context context, AttributeSet attrs) {
+    AbsActionBarView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    AbsActionBarView(Context context, AttributeSet attrs, int defStyle) {
+    AbsActionBarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
         final TypedValue tv = new TypedValue();
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/ActionBarContextView.java b/appcompat/src/main/java/androidx/appcompat/widget/ActionBarContextView.java
index 9bc598c..996ce82 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/ActionBarContextView.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/ActionBarContextView.java
@@ -28,6 +28,8 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
 import androidx.appcompat.view.ActionMode;
@@ -39,8 +41,6 @@
  */
 @RestrictTo(LIBRARY_GROUP_PREFIX)
 public class ActionBarContextView extends AbsActionBarView {
-    private static final String TAG = "ActionBarContextView";
-
     private CharSequence mTitle;
     private CharSequence mSubtitle;
 
@@ -54,15 +54,16 @@
     private boolean mTitleOptional;
     private int mCloseItemLayout;
 
-    public ActionBarContextView(Context context) {
+    public ActionBarContextView(@NonNull Context context) {
         this(context, null);
     }
 
-    public ActionBarContextView(Context context, AttributeSet attrs) {
+    public ActionBarContextView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.actionModeStyle);
     }
 
-    public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) {
+    public ActionBarContextView(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
         final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java b/appcompat/src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java
index b381680..f95483b 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/ActionBarOverlayLayout.java
@@ -37,6 +37,8 @@
 import android.view.Window;
 import android.widget.OverScroller;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
 import androidx.appcompat.app.AppCompatDelegate;
@@ -135,11 +137,11 @@
 
     private final NestedScrollingParentHelper mParentHelper;
 
-    public ActionBarOverlayLayout(Context context) {
+    public ActionBarOverlayLayout(@NonNull Context context) {
         this(context, null);
     }
 
-    public ActionBarOverlayLayout(Context context, AttributeSet attrs) {
+    public ActionBarOverlayLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
         init(context);
 
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/ActionMenuView.java b/appcompat/src/main/java/androidx/appcompat/widget/ActionMenuView.java
index 4b82d87..14fa5b4 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/ActionMenuView.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/ActionMenuView.java
@@ -30,6 +30,7 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.StyleRes;
@@ -72,11 +73,11 @@
 
     OnMenuItemClickListener mOnMenuItemClickListener;
 
-    public ActionMenuView(Context context) {
+    public ActionMenuView(@NonNull Context context) {
         this(context, null);
     }
 
-    public ActionMenuView(Context context, AttributeSet attrs) {
+    public ActionMenuView(@NonNull Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
         setBaselineAligned(false);
         final float density = context.getResources().getDisplayMetrics().density;
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/ActivityChooserView.java b/appcompat/src/main/java/androidx/appcompat/widget/ActivityChooserView.java
index 8f9962792..8f70560 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/ActivityChooserView.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/ActivityChooserView.java
@@ -44,6 +44,8 @@
 import android.widget.PopupWindow;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
 import androidx.appcompat.view.menu.ShowableListMenu;
@@ -197,7 +199,7 @@
      *
      * @param context The application environment.
      */
-    public ActivityChooserView(Context context) {
+    public ActivityChooserView(@NonNull Context context) {
         this(context, null);
     }
 
@@ -207,7 +209,7 @@
      * @param context The application environment.
      * @param attrs A collection of attributes.
      */
-    public ActivityChooserView(Context context, AttributeSet attrs) {
+    public ActivityChooserView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
@@ -218,7 +220,8 @@
      * @param attrs A collection of attributes.
      * @param defStyle The default style to apply to this view.
      */
-    public ActivityChooserView(Context context, AttributeSet attrs, int defStyle) {
+    public ActivityChooserView(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
         TypedArray attributesArray = context.obtainStyledAttributes(attrs,
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java
index 030c41e..b45dd08 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatAutoCompleteTextView.java
@@ -30,6 +30,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
@@ -62,15 +63,16 @@
     private final AppCompatBackgroundHelper mBackgroundTintHelper;
     private final AppCompatTextHelper mTextHelper;
 
-    public AppCompatAutoCompleteTextView(Context context) {
+    public AppCompatAutoCompleteTextView(@NonNull Context context) {
         this(context, null);
     }
 
-    public AppCompatAutoCompleteTextView(Context context, AttributeSet attrs) {
+    public AppCompatAutoCompleteTextView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.autoCompleteTextViewStyle);
     }
 
-    public AppCompatAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatAutoCompleteTextView(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
 
         TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatBackgroundHelper.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatBackgroundHelper.java
index 496efde..310005c 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatBackgroundHelper.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatBackgroundHelper.java
@@ -24,11 +24,13 @@
 import android.view.View;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.appcompat.R;
 import androidx.core.view.ViewCompat;
 
 class AppCompatBackgroundHelper {
 
+    @NonNull
     private final View mView;
     private final AppCompatDrawableManager mDrawableManager;
 
@@ -38,14 +40,19 @@
     private TintInfo mBackgroundTint;
     private TintInfo mTmpInfo;
 
-    AppCompatBackgroundHelper(View view) {
+    AppCompatBackgroundHelper(@NonNull View view) {
         mView = view;
         mDrawableManager = AppCompatDrawableManager.get();
     }
 
-    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
+    void loadFromAttributes(@Nullable AttributeSet attrs, int defStyleAttr) {
         TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
                 R.styleable.ViewBackgroundHelper, defStyleAttr, 0);
+        if (Build.VERSION.SDK_INT >= 29) {
+            mView.saveAttributeDataForStyleable(mView.getContext(),
+                    R.styleable.ViewBackgroundHelper, attrs, a.getWrappedTypeArray(),
+                    defStyleAttr, 0);
+        }
         try {
             if (a.hasValue(R.styleable.ViewBackgroundHelper_android_background)) {
                 mBackgroundResId = a.getResourceId(
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatButton.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatButton.java
index fb1a1f8..202f867 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatButton.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatButton.java
@@ -60,15 +60,16 @@
     private final AppCompatBackgroundHelper mBackgroundTintHelper;
     private final AppCompatTextHelper mTextHelper;
 
-    public AppCompatButton(Context context) {
+    public AppCompatButton(@NonNull Context context) {
         this(context, null);
     }
 
-    public AppCompatButton(Context context, AttributeSet attrs) {
+    public AppCompatButton(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.buttonStyle);
     }
 
-    public AppCompatButton(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatButton(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
 
         mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatCheckBox.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatCheckBox.java
index d5d4019..a544365 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatCheckBox.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatCheckBox.java
@@ -27,6 +27,7 @@
 import android.widget.CheckBox;
 
 import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
@@ -57,15 +58,16 @@
     private final AppCompatBackgroundHelper mBackgroundTintHelper;
     private final AppCompatTextHelper mTextHelper;
 
-    public AppCompatCheckBox(Context context) {
+    public AppCompatCheckBox(@NonNull Context context) {
         this(context, null);
     }
 
-    public AppCompatCheckBox(Context context, AttributeSet attrs) {
+    public AppCompatCheckBox(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.checkboxStyle);
     }
 
-    public AppCompatCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatCheckBox(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
         mCompoundButtonHelper = new AppCompatCompoundButtonHelper(this);
         mCompoundButtonHelper.loadFromAttributes(attrs, defStyleAttr);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatCheckedTextView.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatCheckedTextView.java
index 52772b3..155d413a 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatCheckedTextView.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatCheckedTextView.java
@@ -24,6 +24,8 @@
 import android.widget.CheckedTextView;
 
 import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.appcompat.content.res.AppCompatResources;
 import androidx.core.widget.TextViewCompat;
 
@@ -43,15 +45,16 @@
 
     private final AppCompatTextHelper mTextHelper;
 
-    public AppCompatCheckedTextView(Context context) {
+    public AppCompatCheckedTextView(@NonNull Context context) {
         this(context, null);
     }
 
-    public AppCompatCheckedTextView(Context context, AttributeSet attrs) {
+    public AppCompatCheckedTextView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, android.R.attr.checkedTextViewStyle);
     }
 
-    public AppCompatCheckedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatCheckedTextView(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
 
         mTextHelper = new AppCompatTextHelper(this);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatCompoundButtonHelper.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatCompoundButtonHelper.java
index 05c0532..aa15632 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatCompoundButtonHelper.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatCompoundButtonHelper.java
@@ -25,6 +25,7 @@
 import android.util.AttributeSet;
 import android.widget.CompoundButton;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.appcompat.R;
 import androidx.appcompat.content.res.AppCompatResources;
@@ -32,7 +33,7 @@
 import androidx.core.widget.CompoundButtonCompat;
 
 class AppCompatCompoundButtonHelper {
-
+    @NonNull
     private final CompoundButton mView;
 
     private ColorStateList mButtonTintList = null;
@@ -42,20 +43,17 @@
 
     private boolean mSkipNextApply;
 
-    /**
-     * Interface which allows us to directly set a button, bypass any calls back to ourselves.
-     */
-    interface DirectSetButtonDrawableInterface {
-        void setButtonDrawable(Drawable buttonDrawable);
-    }
-
-    AppCompatCompoundButtonHelper(CompoundButton view) {
+    AppCompatCompoundButtonHelper(@NonNull CompoundButton view) {
         mView = view;
     }
 
-    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
+    void loadFromAttributes(@Nullable AttributeSet attrs, int defStyleAttr) {
         TypedArray a = mView.getContext().obtainStyledAttributes(attrs, R.styleable.CompoundButton,
                 defStyleAttr, 0);
+        if (Build.VERSION.SDK_INT >= 29) {
+            mView.saveAttributeDataForStyleable(mView.getContext(),
+                    R.styleable.CompoundButton, attrs, a, defStyleAttr, 0);
+        }
         try {
             boolean buttonDrawableLoaded = false;
             if (a.hasValue(R.styleable.CompoundButton_buttonCompat)) {
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditText.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditText.java
index 6de13fe..686ba3d 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditText.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatEditText.java
@@ -61,15 +61,16 @@
     private final AppCompatTextHelper mTextHelper;
     private final AppCompatTextClassifierHelper mTextClassifierHelper;
 
-    public AppCompatEditText(Context context) {
+    public AppCompatEditText(@NonNull Context context) {
         this(context, null);
     }
 
-    public AppCompatEditText(Context context, AttributeSet attrs) {
+    public AppCompatEditText(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.editTextStyle);
     }
 
-    public AppCompatEditText(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatEditText(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
 
         mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatImageButton.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatImageButton.java
index 8f41021..bf825bd 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatImageButton.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatImageButton.java
@@ -29,6 +29,7 @@
 import android.widget.ImageView;
 
 import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
@@ -61,15 +62,16 @@
     private final AppCompatBackgroundHelper mBackgroundTintHelper;
     private final AppCompatImageHelper mImageHelper;
 
-    public AppCompatImageButton(Context context) {
+    public AppCompatImageButton(@NonNull Context context) {
         this(context, null);
     }
 
-    public AppCompatImageButton(Context context, AttributeSet attrs) {
+    public AppCompatImageButton(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.imageButtonStyle);
     }
 
-    public AppCompatImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatImageButton(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
 
         mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatImageHelper.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatImageHelper.java
index faf62f7..0ff5247 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatImageHelper.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatImageHelper.java
@@ -36,19 +36,25 @@
  */
 @RestrictTo(LIBRARY_GROUP_PREFIX)
 public class AppCompatImageHelper {
+    @NonNull
     private final ImageView mView;
 
     private TintInfo mInternalImageTint;
     private TintInfo mImageTint;
     private TintInfo mTmpInfo;
 
-    public AppCompatImageHelper(ImageView view) {
+    public AppCompatImageHelper(@NonNull ImageView view) {
         mView = view;
     }
 
     public void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
         TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
                 R.styleable.AppCompatImageView, defStyleAttr, 0);
+        if (Build.VERSION.SDK_INT >= 29) {
+            mView.saveAttributeDataForStyleable(
+                    mView.getContext(), R.styleable.AppCompatImageView, attrs,
+                    a.getWrappedTypeArray(), defStyleAttr, 0);
+        }
         try {
             Drawable drawable = mView.getDrawable();
             if (drawable == null) {
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatImageView.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatImageView.java
index f0a8452..82f0f7e 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatImageView.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatImageView.java
@@ -28,9 +28,9 @@
 import android.widget.ImageView;
 
 import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
-import androidx.appcompat.R;
 import androidx.core.view.TintableBackgroundView;
 import androidx.core.widget.ImageViewCompat;
 import androidx.core.widget.TintableImageSourceView;
@@ -41,12 +41,13 @@
  * <ul>
  *     <li>Allows dynamic tint of its background via the background tint methods in
  *     {@link androidx.core.view.ViewCompat}.</li>
- *     <li>Allows setting of the background tint using {@link R.attr#backgroundTint} and
- *     {@link R.attr#backgroundTintMode}.</li>
+ *     <li>Allows setting of the background tint using
+ *     {@link androidx.appcompat.R.attr#backgroundTint} and
+ *     {@link androidx.appcompat.R.attr#backgroundTintMode}.</li>
  *     <li>Allows dynamic tint of its image via the image tint methods in
  *     {@link ImageViewCompat}.</li>
- *     <li>Allows setting of the image tint using {@link R.attr#tint} and
- *     {@link R.attr#tintMode}.</li>
+ *     <li>Allows setting of the image tint using {@link androidx.appcompat.R.attr#tint} and
+ *     {@link androidx.appcompat.R.attr#tintMode}.</li>
  * </ul>
  *
  * <p>This will automatically be used when you use {@link ImageView} in your layouts
@@ -60,15 +61,16 @@
     private final AppCompatBackgroundHelper mBackgroundTintHelper;
     private final AppCompatImageHelper mImageHelper;
 
-    public AppCompatImageView(Context context) {
+    public AppCompatImageView(@NonNull Context context) {
         this(context, null);
     }
 
-    public AppCompatImageView(Context context, AttributeSet attrs) {
+    public AppCompatImageView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public AppCompatImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatImageView(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
 
         mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
@@ -85,7 +87,7 @@
      *
      * @param resId the resource identifier of the drawable
      * @see ImageView#setImageResource(int)
-     * {@link R.attr#srcCompat}
+     * {@link androidx.appcompat.R.attr#srcCompat}
      */
     @Override
     public void setImageResource(@DrawableRes int resId) {
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView.java
index c92eb4a..0a08c1b 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextView.java
@@ -28,6 +28,7 @@
 import android.widget.MultiAutoCompleteTextView;
 
 import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
@@ -59,15 +60,17 @@
     private final AppCompatBackgroundHelper mBackgroundTintHelper;
     private final AppCompatTextHelper mTextHelper;
 
-    public AppCompatMultiAutoCompleteTextView(Context context) {
+    public AppCompatMultiAutoCompleteTextView(@NonNull Context context) {
         this(context, null);
     }
 
-    public AppCompatMultiAutoCompleteTextView(Context context, AttributeSet attrs) {
+    public AppCompatMultiAutoCompleteTextView(
+            @NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.autoCompleteTextViewStyle);
     }
 
-    public AppCompatMultiAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatMultiAutoCompleteTextView(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
 
         TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatRadioButton.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatRadioButton.java
index 399bf5b..e57bab5 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatRadioButton.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatRadioButton.java
@@ -61,11 +61,11 @@
         this(context, null);
     }
 
-    public AppCompatRadioButton(Context context, AttributeSet attrs) {
+    public AppCompatRadioButton(Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.radioButtonStyle);
     }
 
-    public AppCompatRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatRadioButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
         mCompoundButtonHelper = new AppCompatCompoundButtonHelper(this);
         mCompoundButtonHelper.loadFromAttributes(attrs, defStyleAttr);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatRatingBar.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatRatingBar.java
index 957c57e..0a5afa5 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatRatingBar.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatRatingBar.java
@@ -22,6 +22,8 @@
 import android.view.View;
 import android.widget.RatingBar;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.appcompat.R;
 
 /**
@@ -36,15 +38,16 @@
 
     private final AppCompatProgressBarHelper mAppCompatProgressBarHelper;
 
-    public AppCompatRatingBar(Context context) {
+    public AppCompatRatingBar(@NonNull Context context) {
         this(context, null);
     }
 
-    public AppCompatRatingBar(Context context, AttributeSet attrs) {
+    public AppCompatRatingBar(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.ratingBarStyle);
     }
 
-    public AppCompatRatingBar(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatRatingBar(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
         mAppCompatProgressBarHelper = new AppCompatProgressBarHelper(this);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSeekBar.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSeekBar.java
index bcf37b7..bd362f6 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSeekBar.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSeekBar.java
@@ -21,6 +21,8 @@
 import android.util.AttributeSet;
 import android.widget.SeekBar;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.appcompat.R;
 
 /**
@@ -35,15 +37,16 @@
 
     private final AppCompatSeekBarHelper mAppCompatSeekBarHelper;
 
-    public AppCompatSeekBar(Context context) {
+    public AppCompatSeekBar(@NonNull Context context) {
         this(context, null);
     }
 
-    public AppCompatSeekBar(Context context, AttributeSet attrs) {
+    public AppCompatSeekBar(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.seekBarStyle);
     }
 
-    public AppCompatSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatSeekBar(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
         mAppCompatSeekBarHelper = new AppCompatSeekBarHelper(this);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSeekBarHelper.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSeekBarHelper.java
index 9d1ffc3..1f3ce48 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSeekBarHelper.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSeekBarHelper.java
@@ -20,6 +20,7 @@
 import android.graphics.Canvas;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.util.AttributeSet;
 import android.widget.SeekBar;
 
@@ -49,6 +50,11 @@
 
         TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
                 R.styleable.AppCompatSeekBar, defStyleAttr, 0);
+        if (Build.VERSION.SDK_INT >= 29) {
+            mView.saveAttributeDataForStyleable(mView.getContext(),
+                    R.styleable.AppCompatSeekBar, attrs, a.getWrappedTypeArray(),
+                    defStyleAttr, 0);
+        }
         final Drawable drawable = a.getDrawableIfKnown(R.styleable.AppCompatSeekBar_android_thumb);
         if (drawable != null) {
             mView.setThumb(drawable);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSpinner.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSpinner.java
index 01d7375..92e8aea 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSpinner.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatSpinner.java
@@ -47,6 +47,7 @@
 import android.widget.SpinnerAdapter;
 
 import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
@@ -110,7 +111,8 @@
      * @param context The Context the view is running in, through which it can
      *                access the current theme, resources, etc.
      */
-    public AppCompatSpinner(Context context) {
+    public AppCompatSpinner(
+            @NonNull Context context) {
         this(context, null);
     }
 
@@ -125,7 +127,8 @@
      * @see #MODE_DIALOG
      * @see #MODE_DROPDOWN
      */
-    public AppCompatSpinner(Context context, int mode) {
+    public AppCompatSpinner(
+            @NonNull Context context, int mode) {
         this(context, null, R.attr.spinnerStyle, mode);
     }
 
@@ -136,7 +139,8 @@
      *                access the current theme, resources, etc.
      * @param attrs   The attributes of the XML tag that is inflating the view.
      */
-    public AppCompatSpinner(Context context, AttributeSet attrs) {
+    public AppCompatSpinner(
+            @NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.spinnerStyle);
     }
 
@@ -151,7 +155,8 @@
      *                     reference to a style resource that supplies default values for
      *                     the view. Can be 0 to not look for defaults.
      */
-    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatSpinner(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, MODE_THEME);
     }
 
@@ -170,7 +175,8 @@
      * @see #MODE_DIALOG
      * @see #MODE_DROPDOWN
      */
-    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
+    public AppCompatSpinner(
+            @NonNull Context context, @Nullable  AttributeSet attrs, int defStyleAttr, int mode) {
         this(context, attrs, defStyleAttr, mode, null);
     }
 
@@ -198,8 +204,8 @@
      * @see #MODE_DIALOG
      * @see #MODE_DROPDOWN
      */
-    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode,
-            Resources.Theme popupTheme) {
+    public AppCompatSpinner(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr, int mode, Resources.Theme popupTheme) {
         super(context, attrs, defStyleAttr);
 
         TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextClassifierHelper.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextClassifierHelper.java
index 1470149..c3df776 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextClassifierHelper.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextClassifierHelper.java
@@ -39,7 +39,7 @@
     @Nullable
     private TextClassifier mTextClassifier;
 
-    AppCompatTextClassifierHelper(TextView textView) {
+    AppCompatTextClassifierHelper(@NonNull TextView textView) {
         mTextView = Preconditions.checkNotNull(textView);
     }
 
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
index fb97e5e..697995e 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
@@ -52,6 +52,7 @@
     private static final int SERIF = 2;
     private static final int MONOSPACE = 3;
 
+    @NonNull
     private final TextView mView;
 
     private TintInfo mDrawableLeftTint;
@@ -70,19 +71,25 @@
     private Typeface mFontTypeface;
     private boolean mAsyncFontPending;
 
-    AppCompatTextHelper(TextView view) {
+    AppCompatTextHelper(@NonNull TextView view) {
         mView = view;
         mAutoSizeTextHelper = new AppCompatTextViewAutoSizeHelper(mView);
     }
 
     @SuppressLint("NewApi")
-    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
+    void loadFromAttributes(@Nullable AttributeSet attrs, int defStyleAttr) {
         final Context context = mView.getContext();
         final AppCompatDrawableManager drawableManager = AppCompatDrawableManager.get();
 
         // First read the TextAppearance style id
         TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
                 R.styleable.AppCompatTextHelper, defStyleAttr, 0);
+        if (Build.VERSION.SDK_INT >= 29) {
+            mView.saveAttributeDataForStyleable(mView.getContext(),
+                    R.styleable.AppCompatTextHelper, attrs, a.getWrappedTypeArray(),
+                    defStyleAttr, 0);
+        }
+
         final int ap = a.getResourceId(R.styleable.AppCompatTextHelper_android_textAppearance, -1);
         // Now read the compound drawable and grab any tints
         if (a.hasValue(R.styleable.AppCompatTextHelper_android_drawableLeft)) {
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
index a8e5110..194d121 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
@@ -87,15 +87,16 @@
     @Nullable
     private Future<PrecomputedTextCompat> mPrecomputedTextFuture;
 
-    public AppCompatTextView(Context context) {
+    public AppCompatTextView(@NonNull Context context) {
         this(context, null);
     }
 
-    public AppCompatTextView(Context context, AttributeSet attrs) {
+    public AppCompatTextView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, android.R.attr.textViewStyle);
     }
 
-    public AppCompatTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatTextView(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
 
         mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java
index f5a6c11a..9866069 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java
@@ -99,21 +99,27 @@
     private boolean mHasPresetAutoSizeValues = false;
     private TextPaint mTempTextPaint;
 
+    @NonNull
     private final TextView mTextView;
     private final Context mContext;
 
-    AppCompatTextViewAutoSizeHelper(TextView textView) {
+    AppCompatTextViewAutoSizeHelper(@NonNull TextView textView) {
         mTextView = textView;
         mContext = mTextView.getContext();
     }
 
-    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
+    void loadFromAttributes(@Nullable AttributeSet attrs, int defStyleAttr) {
         float autoSizeMinTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
         float autoSizeMaxTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
         float autoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
 
         TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.AppCompatTextView,
                 defStyleAttr, 0);
+        if (Build.VERSION.SDK_INT >= 29) {
+            mTextView.saveAttributeDataForStyleable(mTextView.getContext(),
+                    R.styleable.AppCompatTextView, attrs, a,
+                    defStyleAttr, 0);
+        }
         if (a.hasValue(R.styleable.AppCompatTextView_autoSizeTextType)) {
             mAutoSizeTextType = a.getInt(R.styleable.AppCompatTextView_autoSizeTextType,
                     TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatToggleButton.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatToggleButton.java
index 29e5387..da2e649 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatToggleButton.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatToggleButton.java
@@ -20,6 +20,9 @@
 import android.util.AttributeSet;
 import android.widget.ToggleButton;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 /**
  * A {@link ToggleButton} which supports compatible features on older versions of the platform,
  * including:
@@ -34,15 +37,16 @@
 
     private final AppCompatTextHelper mTextHelper;
 
-    public AppCompatToggleButton(Context context) {
+    public AppCompatToggleButton(@NonNull Context context) {
         this(context, null);
     }
 
-    public AppCompatToggleButton(Context context, AttributeSet attrs) {
+    public AppCompatToggleButton(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, android.R.attr.buttonStyleToggle);
     }
 
-    public AppCompatToggleButton(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AppCompatToggleButton(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
         mTextHelper = new AppCompatTextHelper(this);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/ButtonBarLayout.java b/appcompat/src/main/java/androidx/appcompat/widget/ButtonBarLayout.java
index 22b3993..bf647a2 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/ButtonBarLayout.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/ButtonBarLayout.java
@@ -25,6 +25,8 @@
 import android.view.View;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
 import androidx.core.view.ViewCompat;
@@ -47,7 +49,7 @@
 
     private int mMinimumHeight = 0;
 
-    public ButtonBarLayout(Context context, AttributeSet attrs) {
+    public ButtonBarLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
         final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonBarLayout);
         if (Build.VERSION.SDK_INT >= 29) {
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/ContentFrameLayout.java b/appcompat/src/main/java/androidx/appcompat/widget/ContentFrameLayout.java
index 3d77e91..fcefd9e 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/ContentFrameLayout.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/ContentFrameLayout.java
@@ -30,6 +30,8 @@
 import android.util.TypedValue;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.core.view.ViewCompat;
 
@@ -55,15 +57,16 @@
 
     private OnAttachListener mAttachListener;
 
-    public ContentFrameLayout(Context context) {
+    public ContentFrameLayout(@NonNull Context context) {
         this(context, null);
     }
 
-    public ContentFrameLayout(Context context, AttributeSet attrs) {
+    public ContentFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public ContentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    public ContentFrameLayout(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mDecorPadding = new Rect();
     }
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/DialogTitle.java b/appcompat/src/main/java/androidx/appcompat/widget/DialogTitle.java
index 63b117b..26dd7d3 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/DialogTitle.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/DialogTitle.java
@@ -24,6 +24,8 @@
 import android.util.AttributeSet;
 import android.util.TypedValue;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
 
@@ -36,15 +38,15 @@
 @RestrictTo(LIBRARY_GROUP_PREFIX)
 public class DialogTitle extends AppCompatTextView {
 
-    public DialogTitle(Context context, AttributeSet attrs, int defStyleAttr) {
+    public DialogTitle(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
     }
 
-    public DialogTitle(Context context, AttributeSet attrs) {
+    public DialogTitle(@NonNull Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
     }
 
-    public DialogTitle(Context context) {
+    public DialogTitle(@NonNull Context context) {
         super(context);
     }
 
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/DropDownListView.java b/appcompat/src/main/java/androidx/appcompat/widget/DropDownListView.java
index be2504e..3acff2c 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/DropDownListView.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/DropDownListView.java
@@ -114,7 +114,7 @@
      *
      * @param context this view's context
      */
-    DropDownListView(Context context, boolean hijackFocus) {
+    DropDownListView(@NonNull Context context, boolean hijackFocus) {
         super(context, null, R.attr.dropDownListViewStyle);
         mHijackFocus = hijackFocus;
         setCacheColorHint(0); // Transparent, since the background drawable could be anything.
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/FitWindowsFrameLayout.java b/appcompat/src/main/java/androidx/appcompat/widget/FitWindowsFrameLayout.java
index 59f590f..2ce02c6 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/FitWindowsFrameLayout.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/FitWindowsFrameLayout.java
@@ -23,6 +23,8 @@
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 
 /**
@@ -33,11 +35,11 @@
 
     private OnFitSystemWindowsListener mListener;
 
-    public FitWindowsFrameLayout(Context context) {
+    public FitWindowsFrameLayout(@NonNull Context context) {
         super(context);
     }
 
-    public FitWindowsFrameLayout(Context context, AttributeSet attrs) {
+    public FitWindowsFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
     }
 
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/FitWindowsLinearLayout.java b/appcompat/src/main/java/androidx/appcompat/widget/FitWindowsLinearLayout.java
index dbea5ba..e578bbe 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/FitWindowsLinearLayout.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/FitWindowsLinearLayout.java
@@ -23,6 +23,8 @@
 import android.util.AttributeSet;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 
 /**
@@ -33,11 +35,11 @@
 
     private OnFitSystemWindowsListener mListener;
 
-    public FitWindowsLinearLayout(Context context) {
+    public FitWindowsLinearLayout(@NonNull Context context) {
         super(context);
     }
 
-    public FitWindowsLinearLayout(Context context, AttributeSet attrs) {
+    public FitWindowsLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
     }
 
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java b/appcompat/src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java
index bd16798..ff3f2b8 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java
@@ -23,6 +23,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
@@ -31,6 +32,8 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
 import androidx.core.view.GravityCompat;
@@ -145,19 +148,25 @@
     private int mShowDividers;
     private int mDividerPadding;
 
-    public LinearLayoutCompat(Context context) {
+    public LinearLayoutCompat(@NonNull Context context) {
         this(context, null);
     }
 
-    public LinearLayoutCompat(Context context, AttributeSet attrs) {
+    public LinearLayoutCompat(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public LinearLayoutCompat(Context context, AttributeSet attrs, int defStyleAttr) {
+    public LinearLayoutCompat(
+            @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
         final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
                 R.styleable.LinearLayoutCompat, defStyleAttr, 0);
+        if (Build.VERSION.SDK_INT >= 29) {
+            saveAttributeDataForStyleable(
+                    context, R.styleable.LinearLayoutCompat, attrs,
+                    a.getWrappedTypeArray(), defStyleAttr, 0);
+        }
 
         int index = a.getInt(R.styleable.LinearLayoutCompat_android_orientation, -1);
         if (index >= 0) {
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/MenuPopupWindow.java b/appcompat/src/main/java/androidx/appcompat/widget/MenuPopupWindow.java
index 8a80b87..b415cd1 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/MenuPopupWindow.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/MenuPopupWindow.java
@@ -33,6 +33,7 @@
 import android.widget.PopupWindow;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.view.menu.ListMenuItemView;
 import androidx.appcompat.view.menu.MenuAdapter;
@@ -68,10 +69,12 @@
 
     private MenuItemHoverListener mHoverListener;
 
-    public MenuPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    public MenuPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
+    @NonNull
     @Override
     DropDownListView createDropDownListView(Context context, boolean hijackFocus) {
         MenuDropDownListView view = new MenuDropDownListView(context, hijackFocus);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java b/appcompat/src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java
index 07da6b9..cdd46c0 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java
@@ -42,6 +42,7 @@
 import android.widget.Spinner;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
 import androidx.appcompat.app.ActionBar;
@@ -78,7 +79,7 @@
 
     private static final int FADE_DURATION = 200;
 
-    public ScrollingTabContainerView(Context context) {
+    public ScrollingTabContainerView(@NonNull Context context) {
         super(context);
 
         setHorizontalScrollBarEnabled(false);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/SearchView.java b/appcompat/src/main/java/androidx/appcompat/widget/SearchView.java
index 4d0ae54..e1fc231 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/SearchView.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/SearchView.java
@@ -68,6 +68,7 @@
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
@@ -263,15 +264,15 @@
         boolean onSuggestionClick(int position);
     }
 
-    public SearchView(Context context) {
+    public SearchView(@NonNull Context context) {
         this(context, null);
     }
 
-    public SearchView(Context context, AttributeSet attrs) {
+    public SearchView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.searchViewStyle);
     }
 
-    public SearchView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public SearchView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
         final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context,
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/SwitchCompat.java b/appcompat/src/main/java/androidx/appcompat/widget/SwitchCompat.java
index f6964a2..fd237f0 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/SwitchCompat.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/SwitchCompat.java
@@ -46,6 +46,7 @@
 import android.widget.CompoundButton;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.appcompat.R;
 import androidx.appcompat.content.res.AppCompatResources;
@@ -190,7 +191,7 @@
      *
      * @param context The Context that will determine this widget's theming.
      */
-    public SwitchCompat(Context context) {
+    public SwitchCompat(@NonNull Context context) {
         this(context, null);
     }
 
@@ -201,7 +202,7 @@
      * @param context The Context that will determine this widget's theming.
      * @param attrs Specification of attributes that should deviate from default styling.
      */
-    public SwitchCompat(Context context, AttributeSet attrs) {
+    public SwitchCompat(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.switchStyle);
     }
 
@@ -215,7 +216,7 @@
      *        reference to a style resource that supplies default values for
      *        the view. Can be 0 to not look for defaults.
      */
-    public SwitchCompat(Context context, AttributeSet attrs, int defStyleAttr) {
+    public SwitchCompat(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
         mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
@@ -225,6 +226,12 @@
 
         final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context,
                 attrs, R.styleable.SwitchCompat, defStyleAttr, 0);
+        if (Build.VERSION.SDK_INT >= 29) {
+            saveAttributeDataForStyleable(
+                    context, R.styleable.SwitchCompat, attrs,
+                    a.getWrappedTypeArray(), defStyleAttr, 0);
+        }
+
         mThumbDrawable = a.getDrawable(R.styleable.SwitchCompat_android_thumb);
         if (mThumbDrawable != null) {
             mThumbDrawable.setCallback(this);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/TintTypedArray.java b/appcompat/src/main/java/androidx/appcompat/widget/TintTypedArray.java
index d0038bc..a365942 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/TintTypedArray.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/TintTypedArray.java
@@ -69,6 +69,13 @@
         mWrapped = array;
     }
 
+    /**
+     * Beware, you very likely do not intend to this method. Proceed with caution.
+     */
+    public TypedArray getWrappedTypeArray() {
+        return mWrapped;
+    }
+
     public Drawable getDrawable(int index) {
         if (mWrapped.hasValue(index)) {
             final int resourceId = mWrapped.getResourceId(index, 0);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java b/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java
index 9b4a11d..5ba7545 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/Toolbar.java
@@ -224,20 +224,25 @@
         }
     };
 
-    public Toolbar(Context context) {
+    public Toolbar(@NonNull Context context) {
         this(context, null);
     }
 
-    public Toolbar(Context context, @Nullable AttributeSet attrs) {
+    public Toolbar(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.toolbarStyle);
     }
 
-    public Toolbar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+    public Toolbar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
         // Need to use getContext() here so that we use the themed context
         final TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
                 R.styleable.Toolbar, defStyleAttr, 0);
+        if (Build.VERSION.SDK_INT >= 29) {
+            saveAttributeDataForStyleable(
+                    context, R.styleable.Toolbar, attrs,
+                    a.getWrappedTypeArray(), defStyleAttr, 0);
+        }
 
         mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0);
         mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/TooltipPopup.java b/appcompat/src/main/java/androidx/appcompat/widget/TooltipPopup.java
index 6697a11..2186447 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/TooltipPopup.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/TooltipPopup.java
@@ -33,6 +33,7 @@
 import android.view.WindowManager;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
 
@@ -55,7 +56,7 @@
     private final int[] mTmpAnchorPos = new int[2];
     private final int[] mTmpAppPos = new int[2];
 
-    TooltipPopup(Context context) {
+    TooltipPopup(@NonNull Context context) {
         mContext = context;
 
         mContentView = LayoutInflater.from(mContext).inflate(R.layout.abc_tooltip, null);
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/ViewStubCompat.java b/appcompat/src/main/java/androidx/appcompat/widget/ViewStubCompat.java
index 4473648..c43a87b 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/ViewStubCompat.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/ViewStubCompat.java
@@ -28,6 +28,8 @@
 import android.view.ViewGroup;
 import android.view.ViewParent;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
 
@@ -49,11 +51,11 @@
     private LayoutInflater mInflater;
     private OnInflateListener mInflateListener;
 
-    public ViewStubCompat(Context context, AttributeSet attrs) {
+    public ViewStubCompat(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public ViewStubCompat(Context context, AttributeSet attrs, int defStyle) {
+    public ViewStubCompat(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewStubCompat,
diff --git a/asynclayoutinflater/build.gradle b/asynclayoutinflater/build.gradle
index 2c12378..3dcd2f0 100644
--- a/asynclayoutinflater/build.gradle
+++ b/asynclayoutinflater/build.gradle
@@ -9,7 +9,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
 }
 
 androidx {
diff --git a/autofill/api/1.0.0-alpha02.txt b/autofill/api/1.0.0-alpha02.txt
index b193fb7..a56ee53 100644
--- a/autofill/api/1.0.0-alpha02.txt
+++ b/autofill/api/1.0.0-alpha02.txt
@@ -38,6 +38,15 @@
     field public static final String AUTOFILL_HINT_POSTAL_ADDRESS_REGION = "addressRegion";
     field public static final String AUTOFILL_HINT_POSTAL_ADDRESS_STREET_ADDRESS = "streetAddress";
     field public static final String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
+    field public static final String AUTOFILL_HINT_SMS_OTP = "smsOTPCode";
+    field public static final String AUTOFILL_HINT_SMS_OTP_1 = "smsOTPCode1";
+    field public static final String AUTOFILL_HINT_SMS_OTP_2 = "smsOTPCode2";
+    field public static final String AUTOFILL_HINT_SMS_OTP_3 = "smsOTPCode3";
+    field public static final String AUTOFILL_HINT_SMS_OTP_4 = "smsOTPCode4";
+    field public static final String AUTOFILL_HINT_SMS_OTP_5 = "smsOTPCode5";
+    field public static final String AUTOFILL_HINT_SMS_OTP_6 = "smsOTPCode6";
+    field public static final String AUTOFILL_HINT_SMS_OTP_7 = "smsOTPCode7";
+    field public static final String AUTOFILL_HINT_SMS_OTP_8 = "smsOTPCode8";
     field public static final String AUTOFILL_HINT_USERNAME = "username";
   }
 
diff --git a/autofill/api/current.txt b/autofill/api/current.txt
index b193fb7..a56ee53 100644
--- a/autofill/api/current.txt
+++ b/autofill/api/current.txt
@@ -38,6 +38,15 @@
     field public static final String AUTOFILL_HINT_POSTAL_ADDRESS_REGION = "addressRegion";
     field public static final String AUTOFILL_HINT_POSTAL_ADDRESS_STREET_ADDRESS = "streetAddress";
     field public static final String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
+    field public static final String AUTOFILL_HINT_SMS_OTP = "smsOTPCode";
+    field public static final String AUTOFILL_HINT_SMS_OTP_1 = "smsOTPCode1";
+    field public static final String AUTOFILL_HINT_SMS_OTP_2 = "smsOTPCode2";
+    field public static final String AUTOFILL_HINT_SMS_OTP_3 = "smsOTPCode3";
+    field public static final String AUTOFILL_HINT_SMS_OTP_4 = "smsOTPCode4";
+    field public static final String AUTOFILL_HINT_SMS_OTP_5 = "smsOTPCode5";
+    field public static final String AUTOFILL_HINT_SMS_OTP_6 = "smsOTPCode6";
+    field public static final String AUTOFILL_HINT_SMS_OTP_7 = "smsOTPCode7";
+    field public static final String AUTOFILL_HINT_SMS_OTP_8 = "smsOTPCode8";
     field public static final String AUTOFILL_HINT_USERNAME = "username";
   }
 
diff --git a/autofill/api/restricted_1.0.0-alpha02.txt b/autofill/api/restricted_1.0.0-alpha02.txt
index b193fb7..a56ee53 100644
--- a/autofill/api/restricted_1.0.0-alpha02.txt
+++ b/autofill/api/restricted_1.0.0-alpha02.txt
@@ -38,6 +38,15 @@
     field public static final String AUTOFILL_HINT_POSTAL_ADDRESS_REGION = "addressRegion";
     field public static final String AUTOFILL_HINT_POSTAL_ADDRESS_STREET_ADDRESS = "streetAddress";
     field public static final String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
+    field public static final String AUTOFILL_HINT_SMS_OTP = "smsOTPCode";
+    field public static final String AUTOFILL_HINT_SMS_OTP_1 = "smsOTPCode1";
+    field public static final String AUTOFILL_HINT_SMS_OTP_2 = "smsOTPCode2";
+    field public static final String AUTOFILL_HINT_SMS_OTP_3 = "smsOTPCode3";
+    field public static final String AUTOFILL_HINT_SMS_OTP_4 = "smsOTPCode4";
+    field public static final String AUTOFILL_HINT_SMS_OTP_5 = "smsOTPCode5";
+    field public static final String AUTOFILL_HINT_SMS_OTP_6 = "smsOTPCode6";
+    field public static final String AUTOFILL_HINT_SMS_OTP_7 = "smsOTPCode7";
+    field public static final String AUTOFILL_HINT_SMS_OTP_8 = "smsOTPCode8";
     field public static final String AUTOFILL_HINT_USERNAME = "username";
   }
 
diff --git a/autofill/api/restricted_current.txt b/autofill/api/restricted_current.txt
index b193fb7..a56ee53 100644
--- a/autofill/api/restricted_current.txt
+++ b/autofill/api/restricted_current.txt
@@ -38,6 +38,15 @@
     field public static final String AUTOFILL_HINT_POSTAL_ADDRESS_REGION = "addressRegion";
     field public static final String AUTOFILL_HINT_POSTAL_ADDRESS_STREET_ADDRESS = "streetAddress";
     field public static final String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
+    field public static final String AUTOFILL_HINT_SMS_OTP = "smsOTPCode";
+    field public static final String AUTOFILL_HINT_SMS_OTP_1 = "smsOTPCode1";
+    field public static final String AUTOFILL_HINT_SMS_OTP_2 = "smsOTPCode2";
+    field public static final String AUTOFILL_HINT_SMS_OTP_3 = "smsOTPCode3";
+    field public static final String AUTOFILL_HINT_SMS_OTP_4 = "smsOTPCode4";
+    field public static final String AUTOFILL_HINT_SMS_OTP_5 = "smsOTPCode5";
+    field public static final String AUTOFILL_HINT_SMS_OTP_6 = "smsOTPCode6";
+    field public static final String AUTOFILL_HINT_SMS_OTP_7 = "smsOTPCode7";
+    field public static final String AUTOFILL_HINT_SMS_OTP_8 = "smsOTPCode8";
     field public static final String AUTOFILL_HINT_USERNAME = "username";
   }
 
diff --git a/autofill/src/main/java/androidx/autofill/HintConstants.java b/autofill/src/main/java/androidx/autofill/HintConstants.java
index adc319e..4c4c5fe 100644
--- a/autofill/src/main/java/androidx/autofill/HintConstants.java
+++ b/autofill/src/main/java/androidx/autofill/HintConstants.java
@@ -542,4 +542,124 @@
      * hints.
      */
     public static final String AUTOFILL_HINT_BIRTH_DATE_YEAR = "birthDateYear";
+
+    /**
+     * Hint indicating that this view can be autofilled with a SMS One Time Password (OTP).
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_SMS_OTP}</code>).
+     *
+     * <p>When annotating OTP code fields which map to a single digit of the code consider using
+     * <code>{@value #AUTOFILL_HINT_SMS_OTP_1}</code> through <code>
+     * {@value #AUTOFILL_HINT_SMS_OTP_8}</code>
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_SMS_OTP = "smsOTPCode";
+
+    /**
+     * Hint indicating that this view can be autofilled with the first character/digit of a SMS One
+     * Time Password (OTP).
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_SMS_OTP_1}</code>).
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_SMS_OTP_1 = "smsOTPCode1";
+
+    /**
+     * Hint indicating that this view can be autofilled with the second character/digit of a SMS One
+     * Time Password (OTP).
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_SMS_OTP_2}</code>).
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_SMS_OTP_2 = "smsOTPCode2";
+
+    /**
+     * Hint indicating that this view can be autofilled with the third character/digit of a SMS One
+     * Time Password (OTP).
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_SMS_OTP_3}</code>).
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_SMS_OTP_3 = "smsOTPCode3";
+
+    /**
+     * Hint indicating that this view can be autofilled with the fourth character/digit of a SMS One
+     * Time Password (OTP).
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_SMS_OTP_4}</code>).
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_SMS_OTP_4 = "smsOTPCode4";
+
+    /**
+     * Hint indicating that this view can be autofilled with the fifth character/digit of a SMS One
+     * Time Password (OTP).
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_SMS_OTP_5}</code>).
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_SMS_OTP_5 = "smsOTPCode5";
+
+    /**
+     * Hint indicating that this view can be autofilled with the sixth character/digit of a SMS One
+     * Time Password (OTP).
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_SMS_OTP_6}</code>).
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_SMS_OTP_6 = "smsOTPCode6";
+
+    /**
+     * Hint indicating that this view can be autofilled with the seventh character/digit of a SMS
+     * One Time Password (OTP).
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_SMS_OTP_7}</code>).
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_SMS_OTP_7 = "smsOTPCode7";
+
+    /**
+     * Hint indicating that this view can be autofilled with the eighth character/digit of a SMS One
+     * Time Password (OTP).
+     *
+     * <p>Can be used with either {@link android.view.View#setAutofillHints(String[])} or <a
+     * href="#attr_android:autofillHint">{@code android:autofillHint}</a> (in which case the value
+     * should be <code>{@value #AUTOFILL_HINT_SMS_OTP_8}</code>).
+     *
+     * <p>See {@link android.view.View#setAutofillHints(String...)} for more info about autofill
+     * hints.
+     */
+    public static final String AUTOFILL_HINT_SMS_OTP_8 = "smsOTPCode8";
 }
diff --git a/benchmark/api/1.0.0-alpha04.txt b/benchmark/api/1.0.0-alpha04.txt
deleted file mode 100644
index 96d4bc8..0000000
--- a/benchmark/api/1.0.0-alpha04.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-// Signature format: 3.0
-package androidx.benchmark {
-
-  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
-    ctor public AndroidBenchmarkRunner();
-  }
-
-  public final class ArgumentsKt {
-    ctor public ArgumentsKt();
-  }
-
-  public final class BenchmarkRule implements org.junit.rules.TestRule {
-    ctor public BenchmarkRule();
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public androidx.benchmark.BenchmarkState getState();
-  }
-
-  public final class BenchmarkRule.Scope {
-    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
-  }
-
-  public final class BenchmarkRuleKt {
-    ctor public BenchmarkRuleKt();
-    method public static inline void measureRepeated(androidx.benchmark.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.BenchmarkRule.Scope,kotlin.Unit> block);
-  }
-
-  public final class BenchmarkState {
-    method public boolean keepRunning();
-    method public void pauseTiming();
-    method public static void reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
-    method public void resumeTiming();
-  }
-
-}
-
diff --git a/benchmark/api/api_lint.ignore b/benchmark/api/api_lint.ignore
deleted file mode 100644
index 0a0591c..0000000
--- a/benchmark/api/api_lint.ignore
+++ /dev/null
@@ -1,7 +0,0 @@
-// Baseline format: 1.0
-DocumentExceptions: androidx.benchmark.BenchmarkRule#getState():
-    Method BenchmarkRule.getState appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
-DocumentExceptions: androidx.benchmark.BenchmarkState#pauseTiming():
-    Method BenchmarkState.pauseTiming appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
-DocumentExceptions: androidx.benchmark.BenchmarkState#resumeTiming():
-    Method BenchmarkState.resumeTiming 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/benchmark/api/current.txt b/benchmark/api/current.txt
deleted file mode 100644
index 96d4bc8..0000000
--- a/benchmark/api/current.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-// Signature format: 3.0
-package androidx.benchmark {
-
-  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
-    ctor public AndroidBenchmarkRunner();
-  }
-
-  public final class ArgumentsKt {
-    ctor public ArgumentsKt();
-  }
-
-  public final class BenchmarkRule implements org.junit.rules.TestRule {
-    ctor public BenchmarkRule();
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public androidx.benchmark.BenchmarkState getState();
-  }
-
-  public final class BenchmarkRule.Scope {
-    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
-  }
-
-  public final class BenchmarkRuleKt {
-    ctor public BenchmarkRuleKt();
-    method public static inline void measureRepeated(androidx.benchmark.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.BenchmarkRule.Scope,kotlin.Unit> block);
-  }
-
-  public final class BenchmarkState {
-    method public boolean keepRunning();
-    method public void pauseTiming();
-    method public static void reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
-    method public void resumeTiming();
-  }
-
-}
-
diff --git a/benchmark/api/restricted_1.0.0-alpha04.txt b/benchmark/api/restricted_1.0.0-alpha04.txt
deleted file mode 100644
index 35c0a19..0000000
--- a/benchmark/api/restricted_1.0.0-alpha04.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-// Signature format: 3.0
-package androidx.benchmark {
-
-  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
-    ctor public AndroidBenchmarkRunner();
-  }
-
-  public final class ArgumentsKt {
-    ctor public ArgumentsKt();
-  }
-
-  public final class BenchmarkRule implements org.junit.rules.TestRule {
-    ctor public BenchmarkRule();
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public androidx.benchmark.BenchmarkState getState();
-  }
-
-  public final class BenchmarkRule.Scope {
-    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
-  }
-
-  public final class BenchmarkRuleKt {
-    ctor public BenchmarkRuleKt();
-    method public static inline void measureRepeated(androidx.benchmark.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.BenchmarkRule.Scope,kotlin.Unit> block);
-  }
-
-  public final class BenchmarkState {
-    method public boolean keepRunning();
-    method public void pauseTiming();
-    method public static void reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
-    method public void resumeTiming();
-  }
-
-
-  public static final class IsolationActivity.Companion {
-    method @AnyThread public void finishSingleton();
-    method @WorkerThread public void launchSingleton();
-  }
-
-}
-
diff --git a/benchmark/api/restricted_current.txt b/benchmark/api/restricted_current.txt
deleted file mode 100644
index 35c0a19..0000000
--- a/benchmark/api/restricted_current.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-// Signature format: 3.0
-package androidx.benchmark {
-
-  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
-    ctor public AndroidBenchmarkRunner();
-  }
-
-  public final class ArgumentsKt {
-    ctor public ArgumentsKt();
-  }
-
-  public final class BenchmarkRule implements org.junit.rules.TestRule {
-    ctor public BenchmarkRule();
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public androidx.benchmark.BenchmarkState getState();
-  }
-
-  public final class BenchmarkRule.Scope {
-    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
-  }
-
-  public final class BenchmarkRuleKt {
-    ctor public BenchmarkRuleKt();
-    method public static inline void measureRepeated(androidx.benchmark.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.BenchmarkRule.Scope,kotlin.Unit> block);
-  }
-
-  public final class BenchmarkState {
-    method public boolean keepRunning();
-    method public void pauseTiming();
-    method public static void reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
-    method public void resumeTiming();
-  }
-
-
-  public static final class IsolationActivity.Companion {
-    method @AnyThread public void finishSingleton();
-    method @WorkerThread public void launchSingleton();
-  }
-
-}
-
diff --git a/benchmark/benchmark/build.gradle b/benchmark/benchmark/build.gradle
index 21a0621..b540dcd 100644
--- a/benchmark/benchmark/build.gradle
+++ b/benchmark/benchmark/build.gradle
@@ -19,10 +19,11 @@
     id("AndroidXPlugin")
     id("com.android.library")
     id("kotlin-android")
+    id("androidx.benchmark")
 }
 
 dependencies {
-    androidTestImplementation(project(":benchmark"))
+    androidTestImplementation(project(":benchmark:benchmark-junit4"))
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(JUNIT)
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 214aea1..050c32d 100644
--- a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/ParameterizedBenchmark.kt
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/ParameterizedBenchmark.kt
@@ -16,8 +16,8 @@
 
 package androidx.benchmark.benchmark
 
-import androidx.benchmark.BenchmarkRule
-import androidx.benchmark.measureRepeated
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
 import androidx.test.filters.LargeTest
 import org.junit.Rule
 import org.junit.Test
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/SynchronizedBenchmark.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/SynchronizedBenchmark.kt
index 5cadaf8..4dcaa59 100644
--- a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/SynchronizedBenchmark.kt
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/SynchronizedBenchmark.kt
@@ -16,8 +16,8 @@
 
 package androidx.benchmark.benchmark
 
-import androidx.benchmark.BenchmarkRule
-import androidx.benchmark.measureRepeated
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
 import androidx.test.filters.LargeTest
 import org.junit.Rule
 import org.junit.Test
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/TrivialJavaBenchmark.java b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/TrivialJavaBenchmark.java
index bfad065..6146758 100644
--- a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/TrivialJavaBenchmark.java
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/TrivialJavaBenchmark.java
@@ -16,8 +16,8 @@
 
 package androidx.benchmark.benchmark;
 
-import androidx.benchmark.BenchmarkRule;
 import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 import androidx.test.filters.LargeTest;
 
 import org.junit.Rule;
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/TrivialKotlinBenchmark.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/TrivialKotlinBenchmark.kt
index c06742f..7260645 100644
--- a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/TrivialKotlinBenchmark.kt
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/TrivialKotlinBenchmark.kt
@@ -16,8 +16,8 @@
 
 package androidx.benchmark.benchmark
 
-import androidx.benchmark.BenchmarkRule
-import androidx.benchmark.measureRepeated
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
 import androidx.test.filters.LargeTest
 import org.junit.Rule
 import org.junit.Test
diff --git a/benchmark/api/1.0.0-alpha01.txt b/benchmark/common/api/1.0.0-alpha01.txt
similarity index 100%
rename from benchmark/api/1.0.0-alpha01.txt
rename to benchmark/common/api/1.0.0-alpha01.txt
diff --git a/benchmark/api/1.0.0-alpha02.txt b/benchmark/common/api/1.0.0-alpha02.txt
similarity index 100%
rename from benchmark/api/1.0.0-alpha02.txt
rename to benchmark/common/api/1.0.0-alpha02.txt
diff --git a/benchmark/api/1.0.0-alpha03.txt b/benchmark/common/api/1.0.0-alpha03.txt
similarity index 100%
rename from benchmark/api/1.0.0-alpha03.txt
rename to benchmark/common/api/1.0.0-alpha03.txt
diff --git a/benchmark/common/api/1.0.0-alpha04.txt b/benchmark/common/api/1.0.0-alpha04.txt
new file mode 100644
index 0000000..6849f29
--- /dev/null
+++ b/benchmark/common/api/1.0.0-alpha04.txt
@@ -0,0 +1,16 @@
+// Signature format: 3.0
+package androidx.benchmark {
+
+  public final class ArgumentsKt {
+    ctor public ArgumentsKt();
+  }
+
+  public final class BenchmarkState {
+    method public boolean keepRunning();
+    method public void pauseTiming();
+    method public static void reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
+    method public void resumeTiming();
+  }
+
+}
+
diff --git a/benchmark/common/api/current.txt b/benchmark/common/api/current.txt
new file mode 100644
index 0000000..6849f29
--- /dev/null
+++ b/benchmark/common/api/current.txt
@@ -0,0 +1,16 @@
+// Signature format: 3.0
+package androidx.benchmark {
+
+  public final class ArgumentsKt {
+    ctor public ArgumentsKt();
+  }
+
+  public final class BenchmarkState {
+    method public boolean keepRunning();
+    method public void pauseTiming();
+    method public static void reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
+    method public void resumeTiming();
+  }
+
+}
+
diff --git a/benchmark/api/res-1.0.0-alpha01.txt b/benchmark/common/api/res-1.0.0-alpha01.txt
similarity index 100%
rename from benchmark/api/res-1.0.0-alpha01.txt
rename to benchmark/common/api/res-1.0.0-alpha01.txt
diff --git a/benchmark/api/res-1.0.0-alpha02.txt b/benchmark/common/api/res-1.0.0-alpha02.txt
similarity index 100%
rename from benchmark/api/res-1.0.0-alpha02.txt
rename to benchmark/common/api/res-1.0.0-alpha02.txt
diff --git a/benchmark/api/res-1.0.0-alpha03.txt b/benchmark/common/api/res-1.0.0-alpha03.txt
similarity index 100%
rename from benchmark/api/res-1.0.0-alpha03.txt
rename to benchmark/common/api/res-1.0.0-alpha03.txt
diff --git a/benchmark/api/res-1.0.0-alpha04.txt b/benchmark/common/api/res-1.0.0-alpha04.txt
similarity index 100%
rename from benchmark/api/res-1.0.0-alpha04.txt
rename to benchmark/common/api/res-1.0.0-alpha04.txt
diff --git a/benchmark/api/restricted_1.0.0-alpha01.txt b/benchmark/common/api/restricted_1.0.0-alpha01.txt
similarity index 100%
rename from benchmark/api/restricted_1.0.0-alpha01.txt
rename to benchmark/common/api/restricted_1.0.0-alpha01.txt
diff --git a/benchmark/api/restricted_1.0.0-alpha02.txt b/benchmark/common/api/restricted_1.0.0-alpha02.txt
similarity index 100%
rename from benchmark/api/restricted_1.0.0-alpha02.txt
rename to benchmark/common/api/restricted_1.0.0-alpha02.txt
diff --git a/benchmark/api/restricted_1.0.0-alpha03.txt b/benchmark/common/api/restricted_1.0.0-alpha03.txt
similarity index 100%
rename from benchmark/api/restricted_1.0.0-alpha03.txt
rename to benchmark/common/api/restricted_1.0.0-alpha03.txt
diff --git a/benchmark/common/api/restricted_1.0.0-alpha04.txt b/benchmark/common/api/restricted_1.0.0-alpha04.txt
new file mode 100644
index 0000000..4165e3e
--- /dev/null
+++ b/benchmark/common/api/restricted_1.0.0-alpha04.txt
@@ -0,0 +1,32 @@
+// Signature format: 3.0
+package androidx.benchmark {
+
+  public final class ArgumentsKt {
+    ctor public ArgumentsKt();
+  }
+
+  public final class BenchmarkState {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public BenchmarkState();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public long getMin();
+    method public boolean keepRunning();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public inline boolean keepRunningInline();
+    method public void pauseTiming();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void report(String fullClassName, String simpleClassName, String methodName);
+    method public static void reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
+    method public void resumeTiming();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class IsolationActivity extends android.app.Activity {
+    method public void actuallyFinish();
+    field public static final androidx.benchmark.IsolationActivity.Companion! Companion;
+  }
+
+  public static final class IsolationActivity.Companion {
+    method @AnyThread public void finishSingleton();
+    method public boolean getResumed();
+    method @WorkerThread public void launchSingleton();
+    property public final boolean resumed;
+  }
+
+}
+
diff --git a/benchmark/common/api/restricted_current.txt b/benchmark/common/api/restricted_current.txt
new file mode 100644
index 0000000..4165e3e
--- /dev/null
+++ b/benchmark/common/api/restricted_current.txt
@@ -0,0 +1,32 @@
+// Signature format: 3.0
+package androidx.benchmark {
+
+  public final class ArgumentsKt {
+    ctor public ArgumentsKt();
+  }
+
+  public final class BenchmarkState {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public BenchmarkState();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public long getMin();
+    method public boolean keepRunning();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public inline boolean keepRunningInline();
+    method public void pauseTiming();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void report(String fullClassName, String simpleClassName, String methodName);
+    method public static void reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0) int warmupIterations, @IntRange(from=0) long thermalThrottleSleepSeconds, @IntRange(from=1) int repeatIterations);
+    method public void resumeTiming();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class IsolationActivity extends android.app.Activity {
+    method public void actuallyFinish();
+    field public static final androidx.benchmark.IsolationActivity.Companion! Companion;
+  }
+
+  public static final class IsolationActivity.Companion {
+    method @AnyThread public void finishSingleton();
+    method public boolean getResumed();
+    method @WorkerThread public void launchSingleton();
+    property public final boolean resumed;
+  }
+
+}
+
diff --git a/benchmark/build.gradle b/benchmark/common/build.gradle
similarity index 85%
rename from benchmark/build.gradle
rename to benchmark/common/build.gradle
index 386f71d..488d327 100644
--- a/benchmark/build.gradle
+++ b/benchmark/common/build.gradle
@@ -26,20 +26,19 @@
 }
 
 dependencies {
-    implementation(ANDROIDX_TEST_RULES)
-    implementation(ANDROIDX_TEST_RUNNER)
     implementation(KOTLIN_STDLIB)
     implementation(SUPPORT_ANNOTATIONS)
+    implementation(ANDROIDX_TEST_MONITOR)
 
-    androidTestImplementation(ANDROIDX_TEST_CORE)
+    androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
 }
 
 androidx {
-    name = "Android Benchmark"
+    name = "Android Benchmark Common"
     publish = Publish.SNAPSHOT_AND_RELEASE
     mavenVersion = LibraryVersions.BENCHMARK
     mavenGroup = LibraryGroups.BENCHMARK
     inceptionYear = "2018"
-    description = "Android Benchmark"
+    description = "Android Benchmark Common"
 }
diff --git a/benchmark/src/androidTest/AndroidManifest.xml b/benchmark/common/src/androidTest/AndroidManifest.xml
similarity index 86%
rename from benchmark/src/androidTest/AndroidManifest.xml
rename to benchmark/common/src/androidTest/AndroidManifest.xml
index f5ec776..bcc3cf4 100644
--- a/benchmark/src/androidTest/AndroidManifest.xml
+++ b/benchmark/common/src/androidTest/AndroidManifest.xml
@@ -16,11 +16,8 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
     package="androidx.benchmark.test">
 
     <application
-        android:name="androidx.benchmark.ArgumentInjectingApplication">
-        <activity android:name="android.app.Activity"/>
-    </application>
+        android:name="androidx.benchmark.ArgumentInjectingApplication"/>
 </manifest>
\ No newline at end of file
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/ArgumentInjectingApplication.kt b/benchmark/common/src/androidTest/java/androidx/benchmark/ArgumentInjectingApplication.kt
similarity index 84%
copy from benchmark/src/androidTest/java/androidx/benchmark/ArgumentInjectingApplication.kt
copy to benchmark/common/src/androidTest/java/androidx/benchmark/ArgumentInjectingApplication.kt
index 30b3bd3..31f45ce 100644
--- a/benchmark/src/androidTest/java/androidx/benchmark/ArgumentInjectingApplication.kt
+++ b/benchmark/common/src/androidTest/java/androidx/benchmark/ArgumentInjectingApplication.kt
@@ -43,9 +43,12 @@
             // Since these benchmark correctness tests run as part of the regular
             // (non-performance-test) suite, they will have debuggable=true, won't be clock-locked,
             // can run with low-battery or on an emulator, and code coverage enabled.
+            // We also don't have the activity up for these correctness tests, instead
+            // leaving testing that behavior to the junit4 module.
             putString(
                 "androidx.benchmark.suppressErrors",
-                "CODE-COVERAGE,DEBUGGABLE,EMULATOR,LOW-BATTERY,UNLOCKED"
+                "ACTIVITY-MISSING,CODE-COVERAGE,DEBUGGABLE,EMULATOR,LOW-BATTERY,UNLOCKED," +
+                        "UNSUSTAINED-ACTIVITY-MISSING"
             )
         }
     }
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt b/benchmark/common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
similarity index 98%
rename from benchmark/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
rename to benchmark/common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
index 2c49b0a..e8ac0ab 100644
--- a/benchmark/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
+++ b/benchmark/common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
@@ -99,7 +99,7 @@
         )
 
         // check attribute presence and naming
-        val prefix = Errors.WARNING_PREFIX
+        val prefix = Errors.PREFIX
         assertNotNull(bundle.get("${prefix}min"))
         assertNotNull(bundle.get("${prefix}mean"))
         assertNotNull(bundle.get("${prefix}count"))
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/CpuInfoTest.kt b/benchmark/common/src/androidTest/java/androidx/benchmark/CpuInfoTest.kt
similarity index 100%
rename from benchmark/src/androidTest/java/androidx/benchmark/CpuInfoTest.kt
rename to benchmark/common/src/androidTest/java/androidx/benchmark/CpuInfoTest.kt
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt b/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
similarity index 97%
rename from benchmark/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
rename to benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
index 20ed927..d48f976 100644
--- a/benchmark/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
+++ b/benchmark/common/src/androidTest/java/androidx/benchmark/ResultWriterTest.kt
@@ -66,7 +66,7 @@
     fun validateJson() {
         val tempFile = tempFolder.newFile()
 
-        val sustainedPerformanceModeInUse = AndroidBenchmarkRunner.sustainedPerformanceModeInUse
+        val sustainedPerformanceModeInUse = IsolationActivity.sustainedPerformanceModeInUse
 
         ResultWriter.writeReport(tempFile, listOf(reportA, reportB))
         assertEquals(
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/StatsTest.kt b/benchmark/common/src/androidTest/java/androidx/benchmark/StatsTest.kt
similarity index 100%
rename from benchmark/src/androidTest/java/androidx/benchmark/StatsTest.kt
rename to benchmark/common/src/androidTest/java/androidx/benchmark/StatsTest.kt
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/WarmupManagerTest.kt b/benchmark/common/src/androidTest/java/androidx/benchmark/WarmupManagerTest.kt
similarity index 100%
rename from benchmark/src/androidTest/java/androidx/benchmark/WarmupManagerTest.kt
rename to benchmark/common/src/androidTest/java/androidx/benchmark/WarmupManagerTest.kt
diff --git a/benchmark/src/main/AndroidManifest.xml b/benchmark/common/src/main/AndroidManifest.xml
similarity index 100%
rename from benchmark/src/main/AndroidManifest.xml
rename to benchmark/common/src/main/AndroidManifest.xml
diff --git a/benchmark/src/main/java/androidx/benchmark/Arguments.kt b/benchmark/common/src/main/java/androidx/benchmark/Arguments.kt
similarity index 93%
rename from benchmark/src/main/java/androidx/benchmark/Arguments.kt
rename to benchmark/common/src/main/java/androidx/benchmark/Arguments.kt
index 75d15ab..e6163bf 100644
--- a/benchmark/src/main/java/androidx/benchmark/Arguments.kt
+++ b/benchmark/common/src/main/java/androidx/benchmark/Arguments.kt
@@ -29,8 +29,9 @@
 var argumentSource: Bundle? = null
 
 internal object Arguments {
-    val startupMode: Boolean
+    val additionalTestOutputDir: String?
     val outputEnable: Boolean
+    val startupMode: Boolean
     val suppressedErrors: Set<String>
 
     init {
@@ -48,5 +49,7 @@
             .map { it.trim() }
             .filter { it.isNotEmpty() }
             .toSet()
+
+        additionalTestOutputDir = arguments.getString("additionalTestOutputDir")
     }
 }
\ No newline at end of file
diff --git a/benchmark/src/main/java/androidx/benchmark/BenchmarkState.kt b/benchmark/common/src/main/java/androidx/benchmark/BenchmarkState.kt
similarity index 90%
rename from benchmark/src/main/java/androidx/benchmark/BenchmarkState.kt
rename to benchmark/common/src/main/java/androidx/benchmark/BenchmarkState.kt
index 4e3b050..7d55169 100644
--- a/benchmark/src/main/java/androidx/benchmark/BenchmarkState.kt
+++ b/benchmark/common/src/main/java/androidx/benchmark/BenchmarkState.kt
@@ -22,9 +22,10 @@
 import android.os.Debug
 import android.util.Log
 import androidx.annotation.IntRange
+import androidx.annotation.RestrictTo
 import androidx.annotation.VisibleForTesting
+import androidx.benchmark.Errors.PREFIX
 import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.Assert.fail
 import java.io.File
 import java.text.NumberFormat
 import java.util.ArrayList
@@ -53,7 +54,12 @@
  *
  * @see BenchmarkRule#getState()
  */
-class BenchmarkState internal constructor() {
+class BenchmarkState {
+    /** @hide */
+    @Suppress("ConvertSecondaryConstructorToPrimary")
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    constructor() {}
+
     private var warmupIteration = 0 // increasing iteration count during warmup
 
     /**
@@ -122,6 +128,14 @@
         }
 
     /**
+     * Used for testing in other modules
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    fun getMin(): Long = stats.min
+
+    /**
      * Stops the benchmark timer.
      *
      * This method can be called only when the timer is running.
@@ -141,6 +155,8 @@
      * }
      * ```
      *
+     * @throws [IllegalStateException] if the benchmark is already paused.
+     *
      * @see resumeTiming
      */
     fun pauseTiming() {
@@ -171,6 +187,9 @@
      *         processBitmap(input);
      *     }
      * }
+     *
+     * @throws [IllegalStateException] if the benchmark is already running.
+     *
      * ```
      *
      * @see pauseTiming
@@ -258,10 +277,12 @@
      * This codepath uses exclusively @JvmField/const members, so there are no method calls at all
      * in the inlined loop. On recent Android Platform versions, ART inlines these accessors anyway,
      * but we want to be sure it's as simple as possible.
+     *
+     * @hide
      */
     @Suppress("NOTHING_TO_INLINE")
-    @PublishedApi
-    internal inline fun keepRunningInline(): Boolean {
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    inline fun keepRunningInline(): Boolean {
         if (iterationsRemaining > 1) {
             iterationsRemaining--
             return true
@@ -292,12 +313,12 @@
         when (state) {
             NOT_STARTED -> {
                 if (Errors.UNSUPPRESSED_WARNING_MESSAGE != null) {
-                    fail(Errors.UNSUPPRESSED_WARNING_MESSAGE)
+                    throw AssertionError(Errors.UNSUPPRESSED_WARNING_MESSAGE)
                 }
-
                 if (!firstBenchmark && Arguments.startupMode) {
-                    fail("Error - multiple benchmarks in startup mode. Only one benchmark " +
-                            "may be run per 'am instrument' call, to ensure result isolation.")
+                    throw AssertionError("Error - multiple benchmarks in startup mode. Only one " +
+                            "benchmark may be run per 'am instrument' call, to ensure result " +
+                            "isolation.")
                 }
                 firstBenchmark = false
 
@@ -307,7 +328,7 @@
                 }
                 if (performThrottleChecks &&
                     !CpuInfo.locked &&
-                    !AndroidBenchmarkRunner.sustainedPerformanceModeInUse &&
+                    !IsolationActivity.sustainedPerformanceModeInUse &&
                     !Errors.isEmulator
                 ) {
                     ThrottleDetector.computeThrottleBaseline()
@@ -393,17 +414,16 @@
         Log.i(TAG, key + summaryLine())
         val status = Bundle()
 
-        val prefix = Errors.WARNING_PREFIX
-        status.putLong("${prefix}median", stats.median)
-        status.putLong("${prefix}mean", stats.mean.toLong())
-        status.putLong("${prefix}min", stats.min)
-        status.putLong("${prefix}standardDeviation", stats.standardDeviation.toLong())
-        status.putLong("${prefix}count", maxIterations.toLong())
+        status.putLong("${PREFIX}median", stats.median)
+        status.putLong("${PREFIX}mean", stats.mean.toLong())
+        status.putLong("${PREFIX}min", stats.min)
+        status.putLong("${PREFIX}standardDeviation", stats.standardDeviation.toLong())
+        status.putLong("${PREFIX}count", maxIterations.toLong())
         status.putIdeSummaryLine(key, stats.min)
         return status
     }
 
-    internal fun sendStatus(testName: String) {
+    private fun sendStatus(testName: String) {
         val bundle = getFullStatusReport(testName)
         InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, bundle)
     }
@@ -420,6 +440,26 @@
         else -> false
     }
 
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    fun report(
+        fullClassName: String,
+        simpleClassName: String,
+        methodName: String
+    ) {
+        val fullTestName = "$PREFIX$simpleClassName.$methodName"
+        sendStatus(fullTestName)
+
+        ResultWriter.appendReport(
+            getReport(
+                testName = PREFIX + methodName,
+                className = fullClassName
+            )
+        )
+    }
+
     internal companion object {
         private const val TAG = "Benchmark"
         private const val STUDIO_OUTPUT_KEY_PREFIX = "android.studio.display."
@@ -483,7 +523,7 @@
 
             // Report value to Studio console
             val bundle = Bundle()
-            val fullTestName = Errors.WARNING_PREFIX +
+            val fullTestName = Errors.PREFIX +
                     if (className.isNotEmpty()) "$className.$testName" else testName
             bundle.putIdeSummaryLine(fullTestName, report.stats.min)
             InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, bundle)
diff --git a/benchmark/src/main/java/androidx/benchmark/CpuInfo.kt b/benchmark/common/src/main/java/androidx/benchmark/CpuInfo.kt
similarity index 100%
rename from benchmark/src/main/java/androidx/benchmark/CpuInfo.kt
rename to benchmark/common/src/main/java/androidx/benchmark/CpuInfo.kt
diff --git a/benchmark/src/main/java/androidx/benchmark/Errors.kt b/benchmark/common/src/main/java/androidx/benchmark/Errors.kt
similarity index 90%
rename from benchmark/src/main/java/androidx/benchmark/Errors.kt
rename to benchmark/common/src/main/java/androidx/benchmark/Errors.kt
index 41c4e90e..ce47af7 100644
--- a/benchmark/src/main/java/androidx/benchmark/Errors.kt
+++ b/benchmark/common/src/main/java/androidx/benchmark/Errors.kt
@@ -26,7 +26,7 @@
 import java.io.File
 
 /**
- * Lazy-initialized test-suite global state for warnings around measurement inaccuracy.
+ * Lazy-initialized test-suite global state for errors around measurement inaccuracy.
  */
 internal object Errors {
     /**
@@ -44,7 +44,7 @@
 
     private const val TAG = "Benchmark"
 
-    val WARNING_PREFIX: String
+    val PREFIX: String
     val UNSUPPRESSED_WARNING_MESSAGE: String?
     private var warningString: String? = null
 
@@ -147,23 +147,24 @@
         }
 
         if (!CpuInfo.locked &&
-            AndroidBenchmarkRunner.isSustainedPerformanceModeSupported() &&
-            !AndroidBenchmarkRunner.sustainedPerformanceModeInUse
+            IsolationActivity.isSustainedPerformanceModeSupported() &&
+            !IsolationActivity.sustainedPerformanceModeInUse
         ) {
-            warningPrefix += "UNSUSTAINED-RUNNER-MISSING_"
+            warningPrefix += "UNSUSTAINED-ACTIVITY-MISSING_"
             warningString += """
-                |WARNING: Cannot use SustainedPerformanceMode without AndroidBenchmarkRunner
+                |WARNING: Cannot use SustainedPerformanceMode without IsolationActivity
                 |    Benchmark running on device that supports Window.setSustainedPerformanceMode,
-                |    but not using the AndroidBenchmarkRunner. This runner is required to limit
-                |    CPU clock max frequency, to prevent thermal throttling. To fix this, add the
-                |    following to your benchmark module-level build.gradle:
+                |    but not launching IsolationActivity via the AndroidBenchmarkRunner. This
+                |    Activity is required to limit CPU clock max frequency, to prevent thermal
+                |    throttling. To fix this, add the following to your benchmark module-level
+                |    build.gradle:
                 |        android.defaultConfig.testInstrumentationRunner
                 |            = "androidx.benchmark.AndroidBenchmarkRunner"
             """.trimMarginWrapNewlines()
-        } else if (!AndroidBenchmarkRunner.runnerInUse) {
-            warningPrefix += "RUNNER-MISSING_"
+        } else if (IsolationActivity.singleton.get() == null) {
+            warningPrefix += "ACTIVITY-MISSING_"
             warningString += """
-                |WARNING: Not using AndroidBenchmarkRunner
+                |WARNING: Not using IsolationActivity via AndroidBenchmarkRunner
                 |    AndroidBenchmarkRunner should be used to isolate benchmarks from interference
                 |    from other visible apps. To fix this, add the following to your module-level
                 |    build.gradle:
@@ -189,13 +190,13 @@
             """.trimMarginWrapNewlines()
         }
 
-        WARNING_PREFIX = warningPrefix
+        PREFIX = warningPrefix
         if (warningString.isNotEmpty()) {
             this.warningString = warningString
             warningString.split("\n").map { Log.w(TAG, it) }
         }
 
-        val warningSet = WARNING_PREFIX
+        val warningSet = PREFIX
             .split('_')
             .filter { it.isNotEmpty() }
             .toSet()
diff --git a/benchmark/common/src/main/java/androidx/benchmark/IsolationActivity.kt b/benchmark/common/src/main/java/androidx/benchmark/IsolationActivity.kt
new file mode 100644
index 0000000..18fe7c7
--- /dev/null
+++ b/benchmark/common/src/main/java/androidx/benchmark/IsolationActivity.kt
@@ -0,0 +1,175 @@
+/*
+ * 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.benchmark
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.app.Application
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import android.os.PowerManager
+import android.os.Process
+import android.util.Log
+import android.widget.TextView
+import androidx.annotation.AnyThread
+import androidx.annotation.RestrictTo
+import androidx.annotation.WorkerThread
+import androidx.test.platform.app.InstrumentationRegistry
+import java.util.concurrent.atomic.AtomicReference
+import kotlin.concurrent.thread
+
+/**
+ * Simple opaque activity used to reduce benchmark interference from other windows.
+ *
+ * For example, sources of potential interference:
+ * - live wallpaper rendering
+ * - homescreen widget updates
+ * - hotword detection
+ * - status bar repaints
+ * - running in background (some cores may be foreground-app only)
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class IsolationActivity : android.app.Activity() {
+    private var destroyed = false
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.isolation_activity)
+
+        // disable launch animation
+        overridePendingTransition(0, 0)
+
+        if (firstInit) {
+            if (!CpuInfo.locked && isSustainedPerformanceModeSupported()) {
+                sustainedPerformanceModeInUse = true
+                application.registerActivityLifecycleCallbacks(sustainedPerfCallbacks)
+
+                // trigger the one missed lifecycle event, from registering the callbacks late
+                sustainedPerfCallbacks.onActivityCreated(this, savedInstanceState)
+
+                // Keep at least one core busy. Together with a single threaded benchmark, this makes
+                // the process get multi-threaded setSustainedPerformanceMode.
+                //
+                // We want to keep to the relatively lower clocks of the multi-threaded benchmark mode
+                // to avoid any benchmarks running at higher clocks than any others.
+                //
+                // Note, thread names have 15 char max in Systrace
+                thread(name = "BenchSpinThread") {
+                    Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST)
+                    while (true) {}
+                }
+            }
+            firstInit = false
+        }
+
+        val old = singleton.getAndSet(this)
+        if (old != null && !old.destroyed && !old.isFinishing) {
+            throw IllegalStateException("Only one IsolationActivity should exist")
+        }
+
+        findViewById<TextView>(R.id.clock_state).text = when {
+            CpuInfo.locked -> "Locked Clocks"
+            sustainedPerformanceModeInUse -> "Sustained Performance Mode"
+            else -> ""
+        }
+    }
+
+    override fun onResume() {
+        super.onResume()
+        resumed = true
+    }
+
+    override fun onPause() {
+        super.onPause()
+        resumed = false
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        destroyed = true
+    }
+
+    /** finish is ignored! we defer until [actuallyFinish] is called. */
+    override fun finish() {
+    }
+
+    fun actuallyFinish() {
+        // disable close animation
+        overridePendingTransition(0, 0)
+        super.finish()
+    }
+
+    companion object {
+        private const val TAG = "Benchmark"
+        internal val singleton = AtomicReference<IsolationActivity>()
+        private var firstInit = true
+        internal var sustainedPerformanceModeInUse = false
+            private set
+        var resumed = false
+            private set
+
+        @WorkerThread
+        fun launchSingleton() {
+            val intent = Intent(Intent.ACTION_MAIN).apply {
+                Log.d(TAG, "launching Benchmark IsolationActivity")
+                setClassName(
+                    InstrumentationRegistry.getInstrumentation().targetContext.packageName,
+                    IsolationActivity::class.java.name
+                )
+                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+            }
+            InstrumentationRegistry.getInstrumentation().startActivitySync(intent)
+        }
+
+        @AnyThread
+        fun finishSingleton() {
+            Log.d(TAG, "Benchmark runner being destroyed, tearing down activities")
+            singleton.getAndSet(null)?.apply {
+                runOnUiThread {
+                    actuallyFinish()
+                }
+            }
+        }
+
+        internal fun isSustainedPerformanceModeSupported(): Boolean =
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                val context = InstrumentationRegistry.getInstrumentation().targetContext
+                val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
+                powerManager.isSustainedPerformanceModeSupported
+            } else {
+                false
+            }
+
+        private val sustainedPerfCallbacks = object : Application.ActivityLifecycleCallbacks {
+            @SuppressLint("NewApi") // window API guarded by [isSustainedPerformanceModeSupported]
+            override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
+                activity.window.setSustainedPerformanceMode(true)
+            }
+            override fun onActivityDestroyed(activity: Activity) {}
+            override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {}
+            override fun onActivityStarted(activity: Activity) {}
+            override fun onActivityStopped(activity: Activity) {}
+            override fun onActivityPaused(activity: Activity) {}
+            override fun onActivityResumed(activity: Activity) {}
+        }
+    }
+}
\ No newline at end of file
diff --git a/benchmark/src/main/java/androidx/benchmark/MemInfo.kt b/benchmark/common/src/main/java/androidx/benchmark/MemInfo.kt
similarity index 100%
rename from benchmark/src/main/java/androidx/benchmark/MemInfo.kt
rename to benchmark/common/src/main/java/androidx/benchmark/MemInfo.kt
diff --git a/benchmark/src/main/java/androidx/benchmark/ResultWriter.kt b/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt
similarity index 92%
rename from benchmark/src/main/java/androidx/benchmark/ResultWriter.kt
rename to benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt
index 06dfb50..6b6ca2e 100644
--- a/benchmark/src/main/java/androidx/benchmark/ResultWriter.kt
+++ b/benchmark/common/src/main/java/androidx/benchmark/ResultWriter.kt
@@ -36,8 +36,10 @@
             // Ideally, append for efficiency
             val packageName =
                 InstrumentationRegistry.getInstrumentation().targetContext!!.packageName
-            @Suppress("DEPRECATION") // b/134925431
-            val filePath = getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS)
+
+            @Suppress("DEPRECATION") // Legacy code path for versions of agp older than 3.6
+            val filePath = Arguments.additionalTestOutputDir?.let { File(it) }
+                ?: getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS)
             val file = File(filePath, "$packageName-benchmarkData.json")
             writeReport(file, reports)
         }
@@ -63,7 +65,7 @@
                 .name("cpuMaxFreqHz").value(CpuInfo.maxFreqHz)
                 .name("memTotalBytes").value(MemInfo.memTotalBytes)
                 .name("sustainedPerformanceModeEnabled")
-                .value(AndroidBenchmarkRunner.sustainedPerformanceModeInUse)
+                .value(IsolationActivity.sustainedPerformanceModeInUse)
             writer.endObject()
 
             writer.name("benchmarks").beginArray()
diff --git a/benchmark/src/main/java/androidx/benchmark/Stats.kt b/benchmark/common/src/main/java/androidx/benchmark/Stats.kt
similarity index 100%
rename from benchmark/src/main/java/androidx/benchmark/Stats.kt
rename to benchmark/common/src/main/java/androidx/benchmark/Stats.kt
diff --git a/benchmark/src/main/java/androidx/benchmark/ThrottleDetector.kt b/benchmark/common/src/main/java/androidx/benchmark/ThrottleDetector.kt
similarity index 100%
rename from benchmark/src/main/java/androidx/benchmark/ThrottleDetector.kt
rename to benchmark/common/src/main/java/androidx/benchmark/ThrottleDetector.kt
diff --git a/benchmark/src/main/java/androidx/benchmark/WarmupManager.kt b/benchmark/common/src/main/java/androidx/benchmark/WarmupManager.kt
similarity index 100%
rename from benchmark/src/main/java/androidx/benchmark/WarmupManager.kt
rename to benchmark/common/src/main/java/androidx/benchmark/WarmupManager.kt
diff --git a/benchmark/src/main/res/drawable-nodpi/logo.png b/benchmark/common/src/main/res/drawable-nodpi/logo.png
similarity index 100%
rename from benchmark/src/main/res/drawable-nodpi/logo.png
rename to benchmark/common/src/main/res/drawable-nodpi/logo.png
Binary files differ
diff --git a/benchmark/src/main/res/layout/isolation_activity.xml b/benchmark/common/src/main/res/layout/isolation_activity.xml
similarity index 100%
rename from benchmark/src/main/res/layout/isolation_activity.xml
rename to benchmark/common/src/main/res/layout/isolation_activity.xml
diff --git a/benchmark/gradle-plugin/build.gradle b/benchmark/gradle-plugin/build.gradle
index 6ce036b..949c11a 100644
--- a/benchmark/gradle-plugin/build.gradle
+++ b/benchmark/gradle-plugin/build.gradle
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-
+import androidx.build.BuildServerConfigurationKt
 import androidx.build.CompilationTarget
 import androidx.build.LibraryGroups
 import androidx.build.LibraryVersions
@@ -70,6 +70,17 @@
 
 tasks["compileTestJava"].dependsOn generateSdkResource
 
+task buildOnServer(type: Copy) {
+  from {
+    def f = project.file("src/main/resources/scripts/lockClocks.sh")
+    if (!f.exists()) {
+        throw new GradleException(f.toString() + " does not exist")
+    }
+    return f
+  }
+  destinationDir BuildServerConfigurationKt.getDistributionDirectory(rootProject)
+}
+
 gradlePlugin {
     plugins {
         benchmark {
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 af03f51..1a57e4f9 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
@@ -21,7 +21,6 @@
 import com.android.build.gradle.LibraryExtension
 import org.gradle.api.Plugin
 import org.gradle.api.Project
-import org.gradle.api.Task
 import org.gradle.api.tasks.StopExecutionException
 
 class BenchmarkPlugin : Plugin<Project> {
@@ -64,33 +63,42 @@
     }
 
     private fun configureWithAndroidExtension(project: Project, extension: BaseExtension) {
+        val defaultConfig = extension.defaultConfig
+        val testInstrumentationArgs = defaultConfig.testInstrumentationRunnerArguments
+
         // 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
-        project.tasks.configureEach {
+        project.configurations.configureEach {
             if (!enabledOutput &&
-                !project.rootProject.hasProperty("android.injected.invoked.from.ide")
+                !project.rootProject.hasProperty("android.injected.invoked.from.ide") &&
+                !testInstrumentationArgs.containsKey("androidx.benchmark.output.enable")
             ) {
                 enabledOutput = true
 
                 // NOTE: This argument is checked by ResultWriter to enable CI reports.
-                extension.defaultConfig.testInstrumentationRunnerArgument(
+                defaultConfig.testInstrumentationRunnerArgument(
                     "androidx.benchmark.output.enable",
                     "true"
                 )
 
-                extension.defaultConfig.testInstrumentationRunnerArgument(
-                    "no-isolated-storage",
-                    "1"
-                )
+                if (!testInstrumentationArgs.containsKey("additionalTestOutputDir")) {
+                    defaultConfig.testInstrumentationRunnerArgument("no-isolated-storage", "1")
+                }
             }
         }
 
-        project.tasks.register("lockClocks", LockClocksTask::class.java).configure {
-            it.adbPath.set(extension.adbExecutable.absolutePath)
+        if (project.rootProject.tasks.findByName("lockClocks") == null) {
+            project.rootProject.tasks.register("lockClocks", LockClocksTask::class.java).configure {
+                it.adbPath.set(extension.adbExecutable.absolutePath)
+            }
         }
-        project.tasks.register("unlockClocks", UnlockClocksTask::class.java).configure {
-            it.adbPath.set(extension.adbExecutable.absolutePath)
+
+        if (project.rootProject.tasks.findByName("unlockClocks") == null) {
+            project.rootProject.tasks.register("unlockClocks", UnlockClocksTask::class.java)
+                .configure {
+                    it.adbPath.set(extension.adbExecutable.absolutePath)
+                }
         }
 
         val extensionVariants = when (extension) {
@@ -110,9 +118,11 @@
         // extension variants have been resolved.
         var applied = false
         extensionVariants.all {
-            if (!applied) {
+            if (!applied && !testInstrumentationArgs.containsKey("additionalTestOutputDir")) {
                 applied = true
 
+                // Only enable pulling benchmark data through this plugin on older versions of AGP
+                // that do not yet enable this flag.
                 project.tasks.register("benchmarkReport", BenchmarkReportTask::class.java)
                     .configure {
                         it.adbPath.set(extension.adbExecutable.absolutePath)
@@ -120,38 +130,12 @@
                     }
 
                 project.tasks.named("connectedAndroidTest").configure {
-                    configureWithConnectedAndroidTest(project, it)
+                    // The task benchmarkReport must be registered by this point, and is responsible
+                    // for pulling report data from all connected devices onto host machine through
+                    // adb.
+                    it.finalizedBy("benchmarkReport")
                 }
             }
         }
     }
-
-    private fun configureWithConnectedAndroidTest(project: Project, connectedAndroidTest: Task) {
-        // The task benchmarkReport must be registered by this point, and is responsible for
-        // pulling report data from all connected devices onto host machine through adb.
-        connectedAndroidTest.finalizedBy("benchmarkReport")
-
-        var hasJetpackBenchmark = false
-
-        project.configurations.matching { it.name.contains("androidTest") }.all {
-            it.allDependencies.all { dependency ->
-                if (dependency.name == "benchmark" && dependency.group == "androidx.benchmark") {
-                    hasJetpackBenchmark = true
-                }
-            }
-        }
-
-        if (!hasJetpackBenchmark) {
-            throw StopExecutionException(
-                """Project ${project.name} missing required project dependency,
-                    androidx.benchmark:benchmark. The androidx.benchmark plugin is meant to be
-                    used in conjunction with the androix.benchmark library, but it was not found
-                    within this project's dependencies. You can add the androidx.benchmark library
-                    to your project by including androidTestImplementation
-                    'androidx.benchmark:benchmark:<version>' in the dependencies block of the
-                    project build.gradle file"""
-                    .trimIndent()
-            )
-        }
-    }
 }
diff --git a/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkReportTask.kt b/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkReportTask.kt
index 8a39554..baae2fc 100644
--- a/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkReportTask.kt
+++ b/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkReportTask.kt
@@ -69,9 +69,7 @@
         for (deviceId in deviceIds) {
             val dataDir = getReportDirForDevice(adb, deviceId)
             if (dataDir.isBlank()) {
-                throw StopExecutionException(
-                    "Failed to find benchmark reports on device: $deviceId"
-                )
+                throw StopExecutionException("Failed to find benchmark report on device: $deviceId")
             }
 
             val outDir = File(benchmarkReportDir, deviceId)
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 4511e38..73a987d 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
@@ -27,6 +27,7 @@
 import java.io.File
 import java.util.Properties
 import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
 import kotlin.test.assertTrue
 
 @RunWith(JUnit4::class)
@@ -109,7 +110,6 @@
         val output = gradleRunner.withArguments("tasks").build()
         assertTrue { output.output.contains("lockClocks - ") }
         assertTrue { output.output.contains("unlockClocks - ") }
-        assertTrue { output.output.contains("benchmarkReport - ") }
     }
 
     @Test
@@ -144,7 +144,6 @@
         val output = gradleRunner.withArguments("tasks").build()
         assertTrue { output.output.contains("lockClocks - ") }
         assertTrue { output.output.contains("unlockClocks - ") }
-        assertTrue { output.output.contains("benchmarkReport - ") }
     }
 
     @Test
@@ -205,9 +204,9 @@
         """.trimIndent()
         )
 
-        assertFailsWith(UnexpectedBuildFailure::class) {
-            gradleRunner.withArguments("-m", "connectedAndroidTest").build()
-        }
+        val output = gradleRunner.withArguments("tasks").build()
+        assertTrue { output.output.contains("lockClocks - ") }
+        assertTrue { output.output.contains("unlockClocks - ") }
     }
 
     @Test
@@ -242,6 +241,89 @@
         val output = gradleRunner.withArguments("tasks").build()
         assertTrue { output.output.contains("lockClocks - ") }
         assertTrue { output.output.contains("unlockClocks - ") }
+    }
+
+    @Test
+    fun applyPluginOnAgp36() {
+        buildFile.writeText(
+            """
+            plugins {
+                id('androidx.benchmark')
+                id('com.android.library')
+            }
+
+            repositories {
+                maven { url "$prebuiltsRepo/androidx/external" }
+                maven { url "$prebuiltsRepo/androidx/internal" }
+            }
+
+            android {
+                compileSdkVersion $compileSdkVersion
+                buildToolsVersion "$buildToolsVersion"
+
+                defaultConfig {
+                    minSdkVersion $minSdkVersion
+                    testInstrumentationRunnerArguments additionalTestOutputDir: "/fake_path/files"
+                }
+            }
+
+            dependencies {
+                androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
+            }
+        """.trimIndent()
+        )
+
+        val output = gradleRunner.withArguments("tasks").build()
+        assertTrue { output.output.contains("lockClocks - ") }
+        assertTrue { output.output.contains("unlockClocks - ") }
+
+        // Should depend on AGP to pull benchmark reports via additionalTestOutputDir.
+        assertFalse { output.output.contains("benchmarkReport - ") }
+    }
+
+    @Test
+    fun applyPluginOnAgp35() {
+        buildFile.writeText(
+            """
+            plugins {
+                id('androidx.benchmark')
+                id('com.android.library')
+            }
+
+            repositories {
+                maven { url "$prebuiltsRepo/androidx/external" }
+                maven { url "$prebuiltsRepo/androidx/internal" }
+            }
+
+            android {
+                compileSdkVersion $compileSdkVersion
+                buildToolsVersion "$buildToolsVersion"
+
+                defaultConfig {
+                    minSdkVersion $minSdkVersion
+                    testInstrumentationRunnerArguments.remove("additionalTestOutputDir")
+                }
+            }
+
+            dependencies {
+                androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
+
+            }
+
+            tasks.register("printInstrumentationArgs") {
+                println android.defaultConfig.testInstrumentationRunnerArguments
+            }
+        """.trimIndent()
+        )
+
+        val output = gradleRunner.withArguments("tasks").build()
+        assertTrue { output.output.contains("lockClocks - ") }
+        assertTrue { output.output.contains("unlockClocks - ") }
+
+        // Should try to pull benchmark reports via legacy BenchmarkPlugin code path.
         assertTrue { output.output.contains("benchmarkReport - ") }
+
+        val argsOutput = gradleRunner.withArguments("printInstrumentationArgs").build()
+        assertTrue { argsOutput.output.contains("no-isolated-storage:1") }
     }
 }
diff --git a/benchmark/integration-tests/startup-benchmark/build.gradle b/benchmark/integration-tests/startup-benchmark/build.gradle
index 3467603..80085a3 100644
--- a/benchmark/integration-tests/startup-benchmark/build.gradle
+++ b/benchmark/integration-tests/startup-benchmark/build.gradle
@@ -20,10 +20,11 @@
     id("AndroidXPlugin")
     id("com.android.library")
     id("kotlin-android")
+    id("androidx.benchmark")
 }
 
 dependencies {
-    androidTestImplementation(project(":benchmark"))
+    androidTestImplementation(project(":benchmark:benchmark-junit4"))
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(JUNIT)
     androidTestImplementation(KOTLIN_STDLIB)
diff --git a/benchmark/integration-tests/startup-benchmark/src/androidTest/java/androidx/benchmark/integration/startup/benchmark/StartupBenchmark.kt b/benchmark/integration-tests/startup-benchmark/src/androidTest/java/androidx/benchmark/integration/startup/benchmark/StartupBenchmark.kt
index aa4f280..d4f9464 100644
--- a/benchmark/integration-tests/startup-benchmark/src/androidTest/java/androidx/benchmark/integration/startup/benchmark/StartupBenchmark.kt
+++ b/benchmark/integration-tests/startup-benchmark/src/androidTest/java/androidx/benchmark/integration/startup/benchmark/StartupBenchmark.kt
@@ -16,8 +16,8 @@
 
 package androidx.benchmark.integration.startup.benchmark
 
-import androidx.benchmark.BenchmarkRule
-import androidx.benchmark.measureRepeated
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
 import androidx.test.filters.LargeTest
 import org.junit.Assert.assertEquals
 import org.junit.Rule
diff --git a/benchmark/junit4/api/1.0.0-alpha04.txt b/benchmark/junit4/api/1.0.0-alpha04.txt
new file mode 100644
index 0000000..756b010
--- /dev/null
+++ b/benchmark/junit4/api/1.0.0-alpha04.txt
@@ -0,0 +1,24 @@
+// Signature format: 3.0
+package androidx.benchmark.junit4 {
+
+  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+    ctor public AndroidBenchmarkRunner();
+  }
+
+  public final class BenchmarkRule implements org.junit.rules.TestRule {
+    ctor public BenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public androidx.benchmark.BenchmarkState getState();
+  }
+
+  public final class BenchmarkRule.Scope {
+    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+  public final class BenchmarkRuleKt {
+    ctor public BenchmarkRuleKt();
+    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+  }
+
+}
+
diff --git a/benchmark/junit4/api/current.txt b/benchmark/junit4/api/current.txt
new file mode 100644
index 0000000..756b010
--- /dev/null
+++ b/benchmark/junit4/api/current.txt
@@ -0,0 +1,24 @@
+// Signature format: 3.0
+package androidx.benchmark.junit4 {
+
+  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+    ctor public AndroidBenchmarkRunner();
+  }
+
+  public final class BenchmarkRule implements org.junit.rules.TestRule {
+    ctor public BenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public androidx.benchmark.BenchmarkState getState();
+  }
+
+  public final class BenchmarkRule.Scope {
+    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+  public final class BenchmarkRuleKt {
+    ctor public BenchmarkRuleKt();
+    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+  }
+
+}
+
diff --git a/benchmark/api/res-1.0.0-alpha04.txt b/benchmark/junit4/api/res-1.0.0-alpha04.txt
similarity index 100%
copy from benchmark/api/res-1.0.0-alpha04.txt
copy to benchmark/junit4/api/res-1.0.0-alpha04.txt
diff --git a/benchmark/junit4/api/restricted_1.0.0-alpha04.txt b/benchmark/junit4/api/restricted_1.0.0-alpha04.txt
new file mode 100644
index 0000000..756b010
--- /dev/null
+++ b/benchmark/junit4/api/restricted_1.0.0-alpha04.txt
@@ -0,0 +1,24 @@
+// Signature format: 3.0
+package androidx.benchmark.junit4 {
+
+  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+    ctor public AndroidBenchmarkRunner();
+  }
+
+  public final class BenchmarkRule implements org.junit.rules.TestRule {
+    ctor public BenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public androidx.benchmark.BenchmarkState getState();
+  }
+
+  public final class BenchmarkRule.Scope {
+    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+  public final class BenchmarkRuleKt {
+    ctor public BenchmarkRuleKt();
+    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+  }
+
+}
+
diff --git a/benchmark/junit4/api/restricted_current.txt b/benchmark/junit4/api/restricted_current.txt
new file mode 100644
index 0000000..756b010
--- /dev/null
+++ b/benchmark/junit4/api/restricted_current.txt
@@ -0,0 +1,24 @@
+// Signature format: 3.0
+package androidx.benchmark.junit4 {
+
+  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+    ctor public AndroidBenchmarkRunner();
+  }
+
+  public final class BenchmarkRule implements org.junit.rules.TestRule {
+    ctor public BenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public androidx.benchmark.BenchmarkState getState();
+  }
+
+  public final class BenchmarkRule.Scope {
+    method public inline <T> T! runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+  public final class BenchmarkRuleKt {
+    ctor public BenchmarkRuleKt();
+    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+  }
+
+}
+
diff --git a/benchmark/build.gradle b/benchmark/junit4/build.gradle
similarity index 75%
copy from benchmark/build.gradle
copy to benchmark/junit4/build.gradle
index 386f71d..6fa16a5 100644
--- a/benchmark/build.gradle
+++ b/benchmark/junit4/build.gradle
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,21 +25,30 @@
     id("kotlin-android")
 }
 
+android {
+    defaultConfig {
+        testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"
+    }
+}
+
 dependencies {
+    api(project(":benchmark:benchmark-common"))
+
+    api(JUNIT)
+    api(KOTLIN_STDLIB)
+
     implementation(ANDROIDX_TEST_RULES)
     implementation(ANDROIDX_TEST_RUNNER)
-    implementation(KOTLIN_STDLIB)
     implementation(SUPPORT_ANNOTATIONS)
 
-    androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
 }
 
 androidx {
-    name = "Android Benchmark"
+    name = "Android Benchmark - JUnit4"
     publish = Publish.SNAPSHOT_AND_RELEASE
     mavenVersion = LibraryVersions.BENCHMARK
     mavenGroup = LibraryGroups.BENCHMARK
-    inceptionYear = "2018"
-    description = "Android Benchmark"
+    inceptionYear = "2019"
+    description = "Android Benchmark - JUnit4"
 }
diff --git a/benchmark/src/androidTest/AndroidManifest.xml b/benchmark/junit4/src/androidTest/AndroidManifest.xml
similarity index 83%
copy from benchmark/src/androidTest/AndroidManifest.xml
copy to benchmark/junit4/src/androidTest/AndroidManifest.xml
index f5ec776..7398a5f 100644
--- a/benchmark/src/androidTest/AndroidManifest.xml
+++ b/benchmark/junit4/src/androidTest/AndroidManifest.xml
@@ -16,11 +16,10 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="androidx.benchmark.test">
+    package="androidx.benchmark.junit4.test">
 
     <application
-        android:name="androidx.benchmark.ArgumentInjectingApplication">
+        android:name="androidx.benchmark.junit4.ArgumentInjectingApplication">
         <activity android:name="android.app.Activity"/>
     </application>
 </manifest>
\ No newline at end of file
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/ActivityBenchmarkTests.kt b/benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/ActivityBenchmarkTests.kt
similarity index 93%
rename from benchmark/src/androidTest/java/androidx/benchmark/ActivityBenchmarkTests.kt
rename to benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/ActivityBenchmarkTests.kt
index a053d3a..d8db6e7 100644
--- a/benchmark/src/androidTest/java/androidx/benchmark/ActivityBenchmarkTests.kt
+++ b/benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/ActivityBenchmarkTests.kt
@@ -14,15 +14,16 @@
  * limitations under the License.
  */
 
-package androidx.benchmark
+package androidx.benchmark.junit4
 
 import android.app.Activity
+import androidx.benchmark.IsolationActivity
 import androidx.test.annotation.UiThreadTest
 import androidx.test.core.app.ActivityScenario
 import androidx.test.ext.junit.rules.ActivityScenarioRule
 import androidx.test.filters.LargeTest
 import androidx.test.rule.ActivityTestRule
-import org.junit.Assert
+import org.junit.Assert.assertFalse
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -31,7 +32,7 @@
 
 fun BenchmarkRule.validateRunWithIsolationActivityHidden() {
     // isolation activity *not* on top
-    Assert.assertFalse(IsolationActivity.singleton.get()!!.resumed)
+    assertFalse(IsolationActivity.resumed)
 
     measureRepeated {}
 }
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/AndroidBenchmarkRunnerTest.kt b/benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/AndroidBenchmarkRunnerTest.kt
similarity index 88%
rename from benchmark/src/androidTest/java/androidx/benchmark/AndroidBenchmarkRunnerTest.kt
rename to benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/AndroidBenchmarkRunnerTest.kt
index 0b1f11f..d5b70f9 100644
--- a/benchmark/src/androidTest/java/androidx/benchmark/AndroidBenchmarkRunnerTest.kt
+++ b/benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/AndroidBenchmarkRunnerTest.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package androidx.benchmark
+package androidx.benchmark.junit4
 
+import androidx.benchmark.IsolationActivity
 import androidx.test.annotation.UiThreadTest
 import androidx.test.filters.SmallTest
 import org.junit.Assert.assertTrue
@@ -29,6 +30,6 @@
     @UiThreadTest
     @Test
     fun checkActivityVisibility() {
-        assertTrue(IsolationActivity.singleton.get()!!.resumed)
+        assertTrue(IsolationActivity.resumed)
     }
 }
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/ArgumentInjectingApplication.kt b/benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/ArgumentInjectingApplication.kt
similarity index 95%
rename from benchmark/src/androidTest/java/androidx/benchmark/ArgumentInjectingApplication.kt
rename to benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/ArgumentInjectingApplication.kt
index 30b3bd3..233fc11 100644
--- a/benchmark/src/androidTest/java/androidx/benchmark/ArgumentInjectingApplication.kt
+++ b/benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/ArgumentInjectingApplication.kt
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-package androidx.benchmark
+package androidx.benchmark.junit4
 
 import android.app.Application
 import android.os.Bundle
+import androidx.benchmark.argumentSource
 
 /**
  * Hack to enable overriding benchmark arguments (since we can't easily do this in CI, per apk)
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/BenchmarkRuleAnnotationTest.kt b/benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleAnnotationTest.kt
similarity index 96%
rename from benchmark/src/androidTest/java/androidx/benchmark/BenchmarkRuleAnnotationTest.kt
rename to benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleAnnotationTest.kt
index c6c8ddc..6a0c62c 100644
--- a/benchmark/src/androidTest/java/androidx/benchmark/BenchmarkRuleAnnotationTest.kt
+++ b/benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleAnnotationTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.benchmark
+package androidx.benchmark.junit4
 
 import androidx.test.filters.SmallTest
 import org.junit.Test
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/BenchmarkRuleTest.kt b/benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleTest.kt
similarity index 93%
rename from benchmark/src/androidTest/java/androidx/benchmark/BenchmarkRuleTest.kt
rename to benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleTest.kt
index 8e988f0..87e5697 100644
--- a/benchmark/src/androidTest/java/androidx/benchmark/BenchmarkRuleTest.kt
+++ b/benchmark/junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.benchmark
+package androidx.benchmark.junit4
 
 import androidx.test.filters.LargeTest
 import org.junit.Assert.assertTrue
@@ -37,7 +37,7 @@
                 Thread.sleep(5)
             }
         }
-        val min = benchmarkRule.getState().stats.min
+        val min = benchmarkRule.getState().getMin()
         assertTrue("minimum $min should be less than 1ms",
             min < TimeUnit.MILLISECONDS.toNanos(1))
     }
diff --git a/media2/widget/src/main/res/values/public.xml b/benchmark/junit4/src/main/AndroidManifest.xml
similarity index 72%
copy from media2/widget/src/main/res/values/public.xml
copy to benchmark/junit4/src/main/AndroidManifest.xml
index 6f7f00f..ee4f8b9 100644
--- a/media2/widget/src/main/res/values/public.xml
+++ b/benchmark/junit4/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright 2018 The Android Open Source Project
+  ~ Copyright (C) 2019 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -14,9 +14,4 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<!-- Definitions of attributes to be exposed as public -->
-<resources>
-    <public type="attr" name="enableControlView" />
-    <public type="attr" name="viewType" />
-</resources>
+<manifest package="androidx.benchmark.junit4"/>
diff --git a/benchmark/junit4/src/main/java/androidx/benchmark/junit4/AndroidBenchmarkRunner.kt b/benchmark/junit4/src/main/java/androidx/benchmark/junit4/AndroidBenchmarkRunner.kt
new file mode 100644
index 0000000..1ca90b3
--- /dev/null
+++ b/benchmark/junit4/src/main/java/androidx/benchmark/junit4/AndroidBenchmarkRunner.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.benchmark.junit4
+
+import androidx.annotation.CallSuper
+import androidx.benchmark.IsolationActivity
+import androidx.test.runner.AndroidJUnitRunner
+
+/**
+ * Instrumentation runner for benchmarks, used to increase stability of measurements and minimize
+ * interference.
+ *
+ * To use this runner, put the following in your module level `build.gradle`:
+ *
+ * ```
+ * android {
+ *     defaultConfig {
+ *         testInstrumentationRunner "androidx.benchmark.AndroidBenchmarkRunner"
+ *     }
+ * }
+ * ```
+ *
+ * ## Minimizing Interference
+ *
+ * This runner launches a simple opaque activity used to reduce benchmark interference from other
+ * windows. Launching other activities is supported e.g. via ActivityTestRule and ActivityScenario -
+ * the opaque activity will be relaunched if not actively running before each test, and after each
+ * test's cleanup is complete.
+ *
+ * For example, sources of potential interference:
+ * - live wallpaper rendering
+ * - homescreen widget updates
+ * - hotword detection
+ * - status bar repaints
+ * - running in background (some cores may be foreground-app only)
+ *
+ * ## Clock Stability
+ *
+ * While it is better for performance stability to lock clocks with the `./gradlew lockClocks` task
+ * provided by the gradle plugin, this is not possible on most devices. The runner provides a
+ * fallback mode for preventing thermal throttling.
+ *
+ * On devices that support [android.view.Window.setSustainedPerformanceMode], the runner will set
+ * this mode on the window of every Activity launched (including the opaque Activity mentioned
+ * above). The runner will also launch a continuously spinning Thread. Together, these ensure that
+ * the app runs in the multithreaded stable performance mode, which locks the maximum clock
+ * frequency to prevent thermal throttling. This ensures stable clock levels across all benchmarks,
+ * even if a continuous suite of benchmarks runs for many minutes on end.
+ */
+@Suppress("unused") // Note: not referenced by code
+open class AndroidBenchmarkRunner : AndroidJUnitRunner() {
+
+    @CallSuper
+    override fun waitForActivitiesToComplete() {
+        // We don't call the super method here, since we have
+        // an activity we intend to persist between tests
+        // TODO: somehow wait for every activity but IsolationActivity
+
+        // Before/After each test, from the test thread, synchronously launch
+        // our IsolationActivity if it's not already resumed
+        var isResumed = false
+        runOnMainSync {
+            isResumed = IsolationActivity.resumed
+        }
+        if (!isResumed) {
+            IsolationActivity.launchSingleton()
+        }
+    }
+
+    @CallSuper
+    override fun onDestroy() {
+        IsolationActivity.finishSingleton()
+        super.waitForActivitiesToComplete()
+        super.onDestroy()
+    }
+}
\ No newline at end of file
diff --git a/benchmark/src/main/java/androidx/benchmark/BenchmarkRule.kt b/benchmark/junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
similarity index 80%
rename from benchmark/src/main/java/androidx/benchmark/BenchmarkRule.kt
rename to benchmark/junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
index 3b3875a..496d536 100644
--- a/benchmark/src/main/java/androidx/benchmark/BenchmarkRule.kt
+++ b/benchmark/junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package androidx.benchmark
+package androidx.benchmark.junit4
 
 import android.Manifest
 import android.util.Log
 import androidx.annotation.RestrictTo
-import androidx.benchmark.Errors.WARNING_PREFIX
+import androidx.benchmark.BenchmarkState
 import androidx.test.rule.GrantPermissionRule
 import org.junit.Assert.assertTrue
 import org.junit.rules.RuleChain
@@ -64,9 +64,9 @@
  * ```
  *
  * Benchmark results will be output:
- * - Summary in AndroidStudio in the test log,
+ * - Summary in AndroidStudio in the test log
+ * - In JSON format, on the host
  * - In simple form in Logcat with the tag "Benchmark"
- * - In csv form in Logcat with the tag "BenchmarkCsv"
  * - To the instrumentation status result Bundle on the gradle command line
  *
  * Every test in the Class using this @Rule must contain a single benchmark.
@@ -106,6 +106,8 @@
      *     ...
      * }
      * ```
+     *
+     * @throws [IllegalStateException] if the BenchmarkRule isn't correctly applied to a test.
      */
     fun getState(): BenchmarkState {
         // Note: this is an explicit method instead of an accessor to help convey it's only for Java
@@ -169,38 +171,37 @@
             .apply(base, description)
     }
 
-    private fun applyInternal(base: Statement, description: Description) = Statement {
-        applied = true
-        var invokeMethodName = description.methodName
-        Log.i(TAG, "Running ${description.className}#$invokeMethodName")
-
-        // validate and simplify the function name.
-        // First, remove the "test" prefix which normally comes from CTS test.
-        // Then make sure the [subTestName] is valid, not just numbers like [0].
-        if (invokeMethodName.startsWith("test")) {
-            assertTrue(
-                "The test name $invokeMethodName is too short",
-                invokeMethodName.length > 5
+    private fun applyInternal(base: Statement, description: Description) =
+        Statement {
+            applied = true
+            var invokeMethodName = description.methodName
+            Log.i(
+                TAG,
+                "Running ${description.className}#$invokeMethodName"
             )
-            invokeMethodName = invokeMethodName.substring(4, 5).toLowerCase() +
-                    invokeMethodName.substring(5)
-        }
 
-        base.evaluate()
-
-        if (enableReport) {
-            val fullTestName =
-                WARNING_PREFIX + description.testClass.simpleName + "." + invokeMethodName
-            internalState.sendStatus(fullTestName)
-
-            ResultWriter.appendReport(
-                internalState.getReport(
-                    testName = WARNING_PREFIX + invokeMethodName,
-                    className = description.className
+            // validate and simplify the function name.
+            // First, remove the "test" prefix which normally comes from CTS test.
+            // Then make sure the [subTestName] is valid, not just numbers like [0].
+            if (invokeMethodName.startsWith("test")) {
+                assertTrue(
+                    "The test name $invokeMethodName is too short",
+                    invokeMethodName.length > 5
                 )
-            )
+                invokeMethodName = invokeMethodName.substring(4, 5).toLowerCase() +
+                        invokeMethodName.substring(5)
+            }
+
+            base.evaluate()
+
+            if (enableReport) {
+                internalState.report(
+                    fullClassName = description.className,
+                    simpleClassName = description.testClass.simpleName,
+                    methodName = invokeMethodName
+                )
+            }
         }
-    }
 
     internal companion object {
         private const val TAG = "BenchmarkRule"
diff --git a/benchmark/src/main/java/androidx/benchmark/AndroidBenchmarkRunner.kt b/benchmark/src/main/java/androidx/benchmark/AndroidBenchmarkRunner.kt
deleted file mode 100644
index 592be43..0000000
--- a/benchmark/src/main/java/androidx/benchmark/AndroidBenchmarkRunner.kt
+++ /dev/null
@@ -1,156 +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.benchmark
-
-import android.annotation.SuppressLint
-import android.app.Activity
-import android.content.Context
-import android.os.Build
-import android.os.Bundle
-import android.os.PowerManager
-import androidx.annotation.CallSuper
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.runner.AndroidJUnitRunner
-import kotlin.concurrent.thread
-
-/**
- * Instrumentation runner for benchmarks, used to increase stability of measurements and minimize
- * interference.
- *
- * To use this runner, put the following in your module level `build.gradle`:
- *
- * ```
- * android {
- *     defaultConfig {
- *         testInstrumentationRunner "androidx.benchmark.AndroidBenchmarkRunner"
- *     }
- * }
- * ```
- *
- * ## Minimizing Interference
- *
- * This runner launches a simple opaque activity used to reduce benchmark interference from other
- * windows. Launching other activities is supported e.g. via ActivityTestRule and ActivityScenario -
- * the opaque activity will be relaunched if not actively running before each test, and after each
- * test's cleanup is complete.
- *
- * For example, sources of potential interference:
- * - live wallpaper rendering
- * - homescreen widget updates
- * - hotword detection
- * - status bar repaints
- * - running in background (some cores may be foreground-app only)
- *
- * ## Clock Stability
- *
- * While it is better for performance stability to lock clocks with the `./gradlew lockClocks` task
- * provided by the gradle plugin, this is not possible on most devices. The runner provides a
- * fallback mode for preventing thermal throttling.
- *
- * On devices that support [android.view.Window.setSustainedPerformanceMode], the runner will set
- * this mode on the window of every Activity launched (including the opaque Activity mentioned
- * above). The runner will also launch a continuously spinning Thread. Together, these ensure that
- * the app runs in the multithreaded stable performance mode, which locks the maximum clock
- * frequency to prevent thermal throttling. This ensures stable clock levels across all benchmarks,
- * even if a continuous suite of benchmarks runs for many minutes on end.
- */
-@Suppress("unused") // Note: not referenced by code
-open class AndroidBenchmarkRunner : AndroidJUnitRunner() {
-    @CallSuper
-    override fun onCreate(arguments: Bundle?) {
-        super.onCreate(arguments)
-
-        // Because these values are used by Errors, it's important to set this flag as early
-        // as possible, before Errors gets lazily initialized. Otherwise we may print false
-        // warnings about needing the runner, when the runner simply hasn't initialized yet.
-        runnerInUse = true
-        sustainedPerformanceModeInUse = !CpuInfo.locked && isSustainedPerformanceModeSupported()
-
-        if (sustainedPerformanceModeInUse) {
-            // Keep at least one core busy. Together with a single threaded benchmark, this makes
-            // the process get multi-threaded setSustainedPerformanceMode.
-            //
-            // We want to keep to the relatively lower clocks of the multi-threaded benchmark mode
-            // to avoid any benchmarks running at higher clocks than any others.
-            //
-            // Note, thread names have 15 char max in Systrace
-            thread(name = "BenchSpinThread") {
-                android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST)
-                while (true) {}
-            }
-        }
-    }
-
-    @CallSuper
-    override fun callActivityOnStart(activity: Activity) {
-        super.callActivityOnStart(activity)
-
-        @SuppressLint("NewApi") // window API guarded by [sustainedPerfMode]
-        if (sustainedPerformanceModeInUse) {
-            activity.window.setSustainedPerformanceMode(true)
-        }
-    }
-
-    @CallSuper
-    override fun waitForActivitiesToComplete() {
-        // We don't call the super method here, since we have
-        // an activity we intend to persist between tests
-        // TODO: somehow wait for every activity but IsolationActivity
-
-        // Before/After each test, from the test thread, synchronously launch
-        // our IsolationActivity if it's not already resumed
-        var isResumed = false
-        runOnMainSync {
-            val activity = IsolationActivity.singleton.get()
-            if (activity != null) {
-                isResumed = activity.resumed
-            }
-        }
-        if (!isResumed) {
-            IsolationActivity.launchSingleton()
-        }
-    }
-
-    @CallSuper
-    override fun onDestroy() {
-        IsolationActivity.finishSingleton()
-        super.waitForActivitiesToComplete()
-        super.onDestroy()
-    }
-
-    internal companion object {
-        /**
-         * Tracks whether Runner is in use.
-         */
-        var runnerInUse = false
-
-        /**
-         * Tracks whether Runner is using [android.view.Window.setSustainedPerformanceMode] to
-         * prevent thermal throttling.
-         */
-        var sustainedPerformanceModeInUse = false
-
-        fun isSustainedPerformanceModeSupported(): Boolean =
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-                val context = InstrumentationRegistry.getInstrumentation().targetContext
-                val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
-                powerManager.isSustainedPerformanceModeSupported
-            } else {
-                false
-            }
-    }
-}
\ No newline at end of file
diff --git a/benchmark/src/main/java/androidx/benchmark/IsolationActivity.kt b/benchmark/src/main/java/androidx/benchmark/IsolationActivity.kt
deleted file mode 100644
index ef10026..0000000
--- a/benchmark/src/main/java/androidx/benchmark/IsolationActivity.kt
+++ /dev/null
@@ -1,118 +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.benchmark
-
-import android.content.Intent
-import android.os.Bundle
-import android.util.Log
-import android.widget.TextView
-import androidx.annotation.AnyThread
-import androidx.annotation.RestrictTo
-import androidx.annotation.WorkerThread
-import androidx.test.platform.app.InstrumentationRegistry
-import java.util.concurrent.atomic.AtomicReference
-
-/**
- * Simple opaque activity used to reduce benchmark interference from other windows.
- *
- * For example, sources of potential interference:
- * - live wallpaper rendering
- * - homescreen widget updates
- * - hotword detection
- * - status bar repaints
- * - running in background (some cores may be foreground-app only)
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-class IsolationActivity : android.app.Activity() {
-    var resumed = false
-    private var destroyed = false
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContentView(R.layout.isolation_activity)
-
-        // disable launch animation
-        overridePendingTransition(0, 0)
-
-        val old = singleton.getAndSet(this)
-        if (old != null && !old.destroyed && !old.isFinishing) {
-            throw IllegalStateException("Only one IsolationActivity should exist")
-        }
-
-        findViewById<TextView>(R.id.clock_state).text = when {
-            CpuInfo.locked -> "Locked Clocks"
-            AndroidBenchmarkRunner.sustainedPerformanceModeInUse -> "Sustained Performance Mode"
-            else -> ""
-        }
-    }
-
-    override fun onResume() {
-        super.onResume()
-        resumed = true
-    }
-
-    override fun onPause() {
-        super.onPause()
-        resumed = false
-    }
-
-    override fun onDestroy() {
-        super.onDestroy()
-        destroyed = true
-    }
-
-    /** finish is ignored! we defer until [actuallyFinish] is called. */
-    override fun finish() {
-    }
-
-    fun actuallyFinish() {
-        // disable close animation
-        overridePendingTransition(0, 0)
-        super.finish()
-    }
-
-    companion object {
-        private const val TAG = "Benchmark"
-        internal val singleton = AtomicReference<IsolationActivity>()
-
-        @WorkerThread
-        fun launchSingleton() {
-            val intent = Intent(Intent.ACTION_MAIN).apply {
-                Log.d(TAG, "launching Benchmark IsolationActivity")
-                setClassName(
-                    InstrumentationRegistry.getInstrumentation().targetContext.packageName,
-                    IsolationActivity::class.java.name
-                )
-                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-                addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
-            }
-            InstrumentationRegistry.getInstrumentation().startActivitySync(intent)
-        }
-
-        @AnyThread
-        fun finishSingleton() {
-            Log.d(TAG, "Benchmark runner being destroyed, tearing down activities")
-            singleton.getAndSet(null)?.apply {
-                runOnUiThread {
-                    actuallyFinish()
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/biometric/build.gradle b/biometric/build.gradle
index e423137..e1dbcfe 100644
--- a/biometric/build.gradle
+++ b/biometric/build.gradle
@@ -10,7 +10,7 @@
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
     api("androidx.appcompat:appcompat:1.1.0-rc01")
-    api("androidx.core:core:1.1.0-rc02")
+    api("androidx.core:core:1.1.0")
     api("androidx.fragment:fragment:1.1.0-rc01")
 }
 
diff --git a/browser/api/1.2.0-alpha06.txt b/browser/api/1.2.0-alpha06.txt
new file mode 100644
index 0000000..a38cd9b
--- /dev/null
+++ b/browser/api/1.2.0-alpha06.txt
@@ -0,0 +1,279 @@
+// Signature format: 3.0
+package androidx.browser.browseractions {
+
+  @Deprecated public class BrowserActionItem {
+    ctor @Deprecated public BrowserActionItem(String, android.app.PendingIntent, @DrawableRes int);
+    ctor @Deprecated public BrowserActionItem(String, android.app.PendingIntent);
+    method @Deprecated public android.app.PendingIntent getAction();
+    method @Deprecated public int getIconId();
+    method @Deprecated public String getTitle();
+  }
+
+  @Deprecated public class BrowserActionsIntent {
+    method @Deprecated public static String? getCreatorPackageName(android.content.Intent);
+    method @Deprecated public android.content.Intent getIntent();
+    method @Deprecated public static String? getUntrustedCreatorPackageName(android.content.Intent);
+    method @Deprecated public static void launchIntent(android.content.Context!, android.content.Intent!);
+    method @Deprecated public static void openBrowserAction(android.content.Context!, android.net.Uri!);
+    method @Deprecated public static void openBrowserAction(android.content.Context!, android.net.Uri!, int, java.util.ArrayList<androidx.browser.browseractions.BrowserActionItem!>!, android.app.PendingIntent!);
+    method @Deprecated public static java.util.List<androidx.browser.browseractions.BrowserActionItem!>! parseBrowserActionItems(java.util.ArrayList<android.os.Bundle!>!);
+    field @Deprecated public static final String ACTION_BROWSER_ACTIONS_OPEN = "androidx.browser.browseractions.browser_action_open";
+    field @Deprecated public static final String EXTRA_APP_ID = "androidx.browser.browseractions.APP_ID";
+    field @Deprecated public static final String EXTRA_MENU_ITEMS = "androidx.browser.browseractions.extra.MENU_ITEMS";
+    field @Deprecated public static final String EXTRA_SELECTED_ACTION_PENDING_INTENT = "androidx.browser.browseractions.extra.SELECTED_ACTION_PENDING_INTENT";
+    field @Deprecated public static final String EXTRA_TYPE = "androidx.browser.browseractions.extra.TYPE";
+    field @Deprecated public static final int ITEM_COPY = 3; // 0x3
+    field @Deprecated public static final int ITEM_DOWNLOAD = 2; // 0x2
+    field @Deprecated public static final int ITEM_INVALID_ITEM = -1; // 0xffffffff
+    field @Deprecated public static final int ITEM_OPEN_IN_INCOGNITO = 1; // 0x1
+    field @Deprecated public static final int ITEM_OPEN_IN_NEW_TAB = 0; // 0x0
+    field @Deprecated public static final int ITEM_SHARE = 4; // 0x4
+    field @Deprecated public static final String KEY_ACTION = "androidx.browser.browseractions.ACTION";
+    field @Deprecated public static final String KEY_ICON_ID = "androidx.browser.browseractions.ICON_ID";
+    field @Deprecated public static final String KEY_TITLE = "androidx.browser.browseractions.TITLE";
+    field @Deprecated public static final int MAX_CUSTOM_ITEMS = 5; // 0x5
+    field @Deprecated public static final int URL_TYPE_AUDIO = 3; // 0x3
+    field @Deprecated public static final int URL_TYPE_FILE = 4; // 0x4
+    field @Deprecated public static final int URL_TYPE_IMAGE = 1; // 0x1
+    field @Deprecated public static final int URL_TYPE_NONE = 0; // 0x0
+    field @Deprecated public static final int URL_TYPE_PLUGIN = 5; // 0x5
+    field @Deprecated public static final int URL_TYPE_VIDEO = 2; // 0x2
+  }
+
+  @Deprecated public static final class BrowserActionsIntent.Builder {
+    ctor @Deprecated public BrowserActionsIntent.Builder(android.content.Context!, android.net.Uri!);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent! build();
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setCustomItems(java.util.ArrayList<androidx.browser.browseractions.BrowserActionItem!>!);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setCustomItems(androidx.browser.browseractions.BrowserActionItem!...);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setOnItemSelectedAction(android.app.PendingIntent!);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setUrlType(int);
+  }
+
+}
+
+package androidx.browser.customtabs {
+
+  public final class CustomTabColorSchemeParams {
+    field @ColorInt public final Integer? navigationBarColor;
+    field @ColorInt public final Integer? secondaryToolbarColor;
+    field @ColorInt public final Integer? toolbarColor;
+  }
+
+  public static final class CustomTabColorSchemeParams.Builder {
+    ctor public CustomTabColorSchemeParams.Builder();
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams build();
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams.Builder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams.Builder setSecondaryToolbarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams.Builder setToolbarColor(@ColorInt int);
+  }
+
+  public class CustomTabsCallback {
+    ctor public CustomTabsCallback();
+    method public void extraCallback(String!, android.os.Bundle!);
+    method public void onMessageChannelReady(android.os.Bundle!);
+    method public void onNavigationEvent(int, android.os.Bundle!);
+    method public void onPostMessage(String!, android.os.Bundle!);
+    method public void onRelationshipValidationResult(@androidx.browser.customtabs.CustomTabsService.Relation int, android.net.Uri!, boolean, android.os.Bundle!);
+    field public static final int NAVIGATION_ABORTED = 4; // 0x4
+    field public static final int NAVIGATION_FAILED = 3; // 0x3
+    field public static final int NAVIGATION_FINISHED = 2; // 0x2
+    field public static final int NAVIGATION_STARTED = 1; // 0x1
+    field public static final int TAB_HIDDEN = 6; // 0x6
+    field public static final int TAB_SHOWN = 5; // 0x5
+  }
+
+  public class CustomTabsClient {
+    method public static boolean bindCustomTabsService(android.content.Context, String?, androidx.browser.customtabs.CustomTabsServiceConnection);
+    method public static boolean connectAndInitialize(android.content.Context, String);
+    method public android.os.Bundle? extraCommand(String, android.os.Bundle?);
+    method public static String? getPackageName(android.content.Context, java.util.List<java.lang.String!>?);
+    method public static String? getPackageName(android.content.Context, java.util.List<java.lang.String!>?, boolean);
+    method public androidx.browser.customtabs.CustomTabsSession? newSession(androidx.browser.customtabs.CustomTabsCallback?);
+    method public androidx.browser.customtabs.CustomTabsSession? newSession(androidx.browser.customtabs.CustomTabsCallback?, int);
+    method public boolean warmup(long);
+  }
+
+  public final class CustomTabsIntent {
+    method public static androidx.browser.customtabs.CustomTabColorSchemeParams getColorSchemeParams(android.content.Intent, int);
+    method public static int getMaxToolbarItems();
+    method public void launchUrl(android.content.Context!, android.net.Uri!);
+    method public static android.content.Intent! setAlwaysUseBrowserUI(android.content.Intent!);
+    method public static boolean shouldAlwaysUseBrowserUI(android.content.Intent!);
+    field public static final int COLOR_SCHEME_DARK = 2; // 0x2
+    field public static final int COLOR_SCHEME_LIGHT = 1; // 0x1
+    field public static final int COLOR_SCHEME_SYSTEM = 0; // 0x0
+    field public static final String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
+    field public static final String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
+    field public static final String EXTRA_COLOR_SCHEME = "androidx.browser.customtabs.extra.COLOR_SCHEME";
+    field public static final String EXTRA_COLOR_SCHEME_PARAMS = "androidx.browser.customtabs.extra.COLOR_SCHEME_PARAMS";
+    field public static final String EXTRA_DEFAULT_SHARE_MENU_ITEM = "android.support.customtabs.extra.SHARE_MENU_ITEM";
+    field public static final String EXTRA_ENABLE_INSTANT_APPS = "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
+    field public static final String EXTRA_ENABLE_URLBAR_HIDING = "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
+    field public static final String EXTRA_EXIT_ANIMATION_BUNDLE = "android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
+    field public static final String EXTRA_MENU_ITEMS = "android.support.customtabs.extra.MENU_ITEMS";
+    field public static final String EXTRA_NAVIGATION_BAR_COLOR = "androidx.browser.customtabs.extra.NAVIGATION_BAR_COLOR";
+    field public static final String EXTRA_REMOTEVIEWS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS";
+    field public static final String EXTRA_REMOTEVIEWS_CLICKED_ID = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID";
+    field public static final String EXTRA_REMOTEVIEWS_PENDINGINTENT = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_PENDINGINTENT";
+    field public static final String EXTRA_REMOTEVIEWS_VIEW_IDS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_VIEW_IDS";
+    field public static final String EXTRA_SECONDARY_TOOLBAR_COLOR = "android.support.customtabs.extra.SECONDARY_TOOLBAR_COLOR";
+    field public static final String EXTRA_SESSION = "android.support.customtabs.extra.SESSION";
+    field public static final String EXTRA_TINT_ACTION_BUTTON = "android.support.customtabs.extra.TINT_ACTION_BUTTON";
+    field public static final String EXTRA_TITLE_VISIBILITY_STATE = "android.support.customtabs.extra.TITLE_VISIBILITY";
+    field public static final String EXTRA_TOOLBAR_COLOR = "android.support.customtabs.extra.TOOLBAR_COLOR";
+    field public static final String EXTRA_TOOLBAR_ITEMS = "android.support.customtabs.extra.TOOLBAR_ITEMS";
+    field public static final String KEY_DESCRIPTION = "android.support.customtabs.customaction.DESCRIPTION";
+    field public static final String KEY_ICON = "android.support.customtabs.customaction.ICON";
+    field public static final String KEY_ID = "android.support.customtabs.customaction.ID";
+    field public static final String KEY_MENU_ITEM_TITLE = "android.support.customtabs.customaction.MENU_ITEM_TITLE";
+    field public static final String KEY_PENDING_INTENT = "android.support.customtabs.customaction.PENDING_INTENT";
+    field public static final int NO_TITLE = 0; // 0x0
+    field public static final int SHOW_PAGE_TITLE = 1; // 0x1
+    field public static final int TOOLBAR_ACTION_BUTTON_ID = 0; // 0x0
+    field public final android.content.Intent intent;
+    field public final android.os.Bundle? startAnimationBundle;
+  }
+
+  public static final class CustomTabsIntent.Builder {
+    ctor public CustomTabsIntent.Builder();
+    ctor public CustomTabsIntent.Builder(androidx.browser.customtabs.CustomTabsSession?);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder addDefaultShareMenuItem();
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder addMenuItem(String, android.app.PendingIntent);
+    method @Deprecated public androidx.browser.customtabs.CustomTabsIntent.Builder addToolbarItem(int, android.graphics.Bitmap, String, android.app.PendingIntent!) throws java.lang.IllegalStateException;
+    method public androidx.browser.customtabs.CustomTabsIntent build();
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder enableUrlBarHiding();
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, String, android.app.PendingIntent, boolean);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, String, android.app.PendingIntent);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setCloseButtonIcon(android.graphics.Bitmap);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setColorScheme(int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setColorSchemeParams(int, androidx.browser.customtabs.CustomTabColorSchemeParams);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setExitAnimations(android.content.Context, @AnimRes int, @AnimRes int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setInstantAppsEnabled(boolean);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSecondaryToolbarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSecondaryToolbarViews(android.widget.RemoteViews, int[]?, android.app.PendingIntent?);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSession(androidx.browser.customtabs.CustomTabsSession);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setShowTitle(boolean);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setStartAnimations(android.content.Context, @AnimRes int, @AnimRes int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setToolbarColor(@ColorInt int);
+  }
+
+  public abstract class CustomTabsService extends android.app.Service {
+    ctor public CustomTabsService();
+    method protected boolean cleanUpSession(androidx.browser.customtabs.CustomTabsSessionToken!);
+    method protected abstract android.os.Bundle! extraCommand(String!, android.os.Bundle!);
+    method protected abstract boolean mayLaunchUrl(androidx.browser.customtabs.CustomTabsSessionToken!, android.net.Uri!, android.os.Bundle!, java.util.List<android.os.Bundle!>!);
+    method protected abstract boolean newSession(androidx.browser.customtabs.CustomTabsSessionToken!);
+    method public android.os.IBinder! onBind(android.content.Intent!);
+    method @androidx.browser.customtabs.CustomTabsService.Result protected abstract int postMessage(androidx.browser.customtabs.CustomTabsSessionToken!, String!, android.os.Bundle!);
+    method protected abstract boolean receiveFile(androidx.browser.customtabs.CustomTabsSessionToken, android.net.Uri, int, android.os.Bundle?);
+    method protected abstract boolean requestPostMessageChannel(androidx.browser.customtabs.CustomTabsSessionToken!, android.net.Uri!);
+    method protected abstract boolean updateVisuals(androidx.browser.customtabs.CustomTabsSessionToken!, android.os.Bundle!);
+    method protected abstract boolean validateRelationship(androidx.browser.customtabs.CustomTabsSessionToken!, @androidx.browser.customtabs.CustomTabsService.Relation int, android.net.Uri!, android.os.Bundle!);
+    method protected abstract boolean warmup(long);
+    field public static final String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";
+    field public static final String CATEGORY_COLOR_SCHEME_CUSTOMIZATION = "androidx.browser.customtabs.category.ColorSchemeCustomization";
+    field public static final String CATEGORY_NAVBAR_COLOR_CUSTOMIZATION = "androidx.browser.customtabs.category.NavBarColorCustomization";
+    field public static final int FILE_PURPOSE_TRUSTED_WEB_ACTIVITY_SPLASH_IMAGE = 1; // 0x1
+    field public static final String KEY_URL = "android.support.customtabs.otherurls.URL";
+    field public static final int RELATION_HANDLE_ALL_URLS = 2; // 0x2
+    field public static final int RELATION_USE_AS_ORIGIN = 1; // 0x1
+    field public static final int RESULT_FAILURE_DISALLOWED = -1; // 0xffffffff
+    field public static final int RESULT_FAILURE_MESSAGING_ERROR = -3; // 0xfffffffd
+    field public static final int RESULT_FAILURE_REMOTE_ERROR = -2; // 0xfffffffe
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+    field public static final String TRUSTED_WEB_ACTIVITY_CATEGORY = "androidx.browser.trusted.category.TrustedWebActivities";
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({androidx.browser.customtabs.CustomTabsService.RELATION_USE_AS_ORIGIN, androidx.browser.customtabs.CustomTabsService.RELATION_HANDLE_ALL_URLS}) public static @interface CustomTabsService.Relation {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({androidx.browser.customtabs.CustomTabsService.RESULT_SUCCESS, androidx.browser.customtabs.CustomTabsService.RESULT_FAILURE_DISALLOWED, androidx.browser.customtabs.CustomTabsService.RESULT_FAILURE_REMOTE_ERROR, androidx.browser.customtabs.CustomTabsService.RESULT_FAILURE_MESSAGING_ERROR}) public static @interface CustomTabsService.Result {
+  }
+
+  public abstract class CustomTabsServiceConnection implements android.content.ServiceConnection {
+    ctor public CustomTabsServiceConnection();
+    method public abstract void onCustomTabsServiceConnected(android.content.ComponentName!, androidx.browser.customtabs.CustomTabsClient!);
+    method public final void onServiceConnected(android.content.ComponentName!, android.os.IBinder!);
+  }
+
+  public final class CustomTabsSession {
+    method @VisibleForTesting public static androidx.browser.customtabs.CustomTabsSession createMockSessionForTesting(android.content.ComponentName);
+    method public boolean mayLaunchUrl(android.net.Uri!, android.os.Bundle!, java.util.List<android.os.Bundle!>!);
+    method @androidx.browser.customtabs.CustomTabsService.Result public int postMessage(String!, android.os.Bundle!);
+    method public boolean receiveFile(android.net.Uri, int, android.os.Bundle?);
+    method public boolean requestPostMessageChannel(android.net.Uri!);
+    method public boolean setActionButton(android.graphics.Bitmap, String);
+    method public boolean setSecondaryToolbarViews(android.widget.RemoteViews?, int[]?, android.app.PendingIntent?);
+    method @Deprecated public boolean setToolbarItem(int, android.graphics.Bitmap, String);
+    method public boolean validateRelationship(@androidx.browser.customtabs.CustomTabsService.Relation int, android.net.Uri, android.os.Bundle?);
+  }
+
+  public class CustomTabsSessionToken {
+    method public static androidx.browser.customtabs.CustomTabsSessionToken createMockSessionTokenForTesting();
+    method public androidx.browser.customtabs.CustomTabsCallback? getCallback();
+    method public static androidx.browser.customtabs.CustomTabsSessionToken? getSessionTokenFromIntent(android.content.Intent);
+    method public boolean isAssociatedWith(androidx.browser.customtabs.CustomTabsSession);
+  }
+
+  public class PostMessageService extends android.app.Service {
+    ctor public PostMessageService();
+    method public android.os.IBinder! onBind(android.content.Intent!);
+  }
+
+  public abstract class PostMessageServiceConnection implements android.content.ServiceConnection {
+    ctor public PostMessageServiceConnection(androidx.browser.customtabs.CustomTabsSessionToken!);
+    method public boolean bindSessionToPostMessageService(android.content.Context!, String!);
+    method public final boolean notifyMessageChannelReady(android.os.Bundle!);
+    method public void onPostMessageServiceConnected();
+    method public void onPostMessageServiceDisconnected();
+    method public final void onServiceConnected(android.content.ComponentName!, android.os.IBinder!);
+    method public final void onServiceDisconnected(android.content.ComponentName!);
+    method public final boolean postMessage(String!, android.os.Bundle!);
+    method public void unbindFromContext(android.content.Context!);
+  }
+
+  public class TrustedWebUtils {
+    method @Deprecated public static void launchAsTrustedWebActivity(android.content.Context, androidx.browser.customtabs.CustomTabsIntent, android.net.Uri);
+    method public static boolean splashScreensAreSupported(android.content.Context, String, String);
+    method @WorkerThread public static boolean transferSplashImage(android.content.Context, java.io.File, String, String, androidx.browser.customtabs.CustomTabsSession);
+    field public static final String EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY = "android.support.customtabs.extra.LAUNCH_AS_TRUSTED_WEB_ACTIVITY";
+  }
+
+}
+
+package androidx.browser.trusted {
+
+  public class TrustedWebActivityIntentBuilder {
+    ctor public TrustedWebActivityIntentBuilder(android.net.Uri);
+    method public android.content.Intent build(androidx.browser.customtabs.CustomTabsSession);
+    method public androidx.browser.customtabs.CustomTabsIntent buildCustomTabsIntent();
+    method public android.net.Uri getUrl();
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setAdditionalTrustedOrigins(java.util.List<java.lang.String!>);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setColorScheme(int);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setColorSchemeParams(int, androidx.browser.customtabs.CustomTabColorSchemeParams);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setSplashScreenParams(android.os.Bundle);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setToolbarColor(@ColorInt int);
+    field public static final String EXTRA_ADDITIONAL_TRUSTED_ORIGINS = "android.support.customtabs.extra.ADDITIONAL_TRUSTED_ORIGINS";
+    field public static final String EXTRA_SPLASH_SCREEN_PARAMS = "androidx.browser.trusted.EXTRA_SPLASH_SCREEN_PARAMS";
+  }
+
+}
+
+package androidx.browser.trusted.splashscreens {
+
+  public final class SplashScreenParamKey {
+    field public static final String BACKGROUND_COLOR = "androidx.browser.trusted.trusted.KEY_SPLASH_SCREEN_BACKGROUND_COLOR";
+    field public static final String FADE_OUT_DURATION_MS = "androidx.browser.trusted.KEY_SPLASH_SCREEN_FADE_OUT_DURATION";
+    field public static final String IMAGE_TRANSFORMATION_MATRIX = "androidx.browser.trusted.KEY_SPLASH_SCREEN_TRANSFORMATION_MATRIX";
+    field public static final String SCALE_TYPE = "androidx.browser.trusted.KEY_SPLASH_SCREEN_SCALE_TYPE";
+    field public static final String VERSION = "androidx.browser.trusted.KEY_SPLASH_SCREEN_VERSION";
+  }
+
+  public final class SplashScreenVersion {
+    field public static final String V1 = "androidx.browser.trusted.category.TrustedWebActivitySplashScreensV1";
+  }
+
+}
+
diff --git a/browser/api/1.2.0-alpha07.txt b/browser/api/1.2.0-alpha07.txt
new file mode 100644
index 0000000..798e756
--- /dev/null
+++ b/browser/api/1.2.0-alpha07.txt
@@ -0,0 +1,316 @@
+// Signature format: 3.0
+package androidx.browser.browseractions {
+
+  @Deprecated public class BrowserActionItem {
+    ctor @Deprecated public BrowserActionItem(String, android.app.PendingIntent, @DrawableRes int);
+    ctor @Deprecated public BrowserActionItem(String, android.app.PendingIntent);
+    method @Deprecated public android.app.PendingIntent getAction();
+    method @Deprecated public int getIconId();
+    method @Deprecated public String getTitle();
+  }
+
+  @Deprecated public class BrowserActionsIntent {
+    method @Deprecated public static String? getCreatorPackageName(android.content.Intent);
+    method @Deprecated public android.content.Intent getIntent();
+    method @Deprecated public static String? getUntrustedCreatorPackageName(android.content.Intent);
+    method @Deprecated public static void launchIntent(android.content.Context!, android.content.Intent!);
+    method @Deprecated public static void openBrowserAction(android.content.Context!, android.net.Uri!);
+    method @Deprecated public static void openBrowserAction(android.content.Context!, android.net.Uri!, int, java.util.ArrayList<androidx.browser.browseractions.BrowserActionItem!>!, android.app.PendingIntent!);
+    method @Deprecated public static java.util.List<androidx.browser.browseractions.BrowserActionItem!>! parseBrowserActionItems(java.util.ArrayList<android.os.Bundle!>!);
+    field @Deprecated public static final String ACTION_BROWSER_ACTIONS_OPEN = "androidx.browser.browseractions.browser_action_open";
+    field @Deprecated public static final String EXTRA_APP_ID = "androidx.browser.browseractions.APP_ID";
+    field @Deprecated public static final String EXTRA_MENU_ITEMS = "androidx.browser.browseractions.extra.MENU_ITEMS";
+    field @Deprecated public static final String EXTRA_SELECTED_ACTION_PENDING_INTENT = "androidx.browser.browseractions.extra.SELECTED_ACTION_PENDING_INTENT";
+    field @Deprecated public static final String EXTRA_TYPE = "androidx.browser.browseractions.extra.TYPE";
+    field @Deprecated public static final int ITEM_COPY = 3; // 0x3
+    field @Deprecated public static final int ITEM_DOWNLOAD = 2; // 0x2
+    field @Deprecated public static final int ITEM_INVALID_ITEM = -1; // 0xffffffff
+    field @Deprecated public static final int ITEM_OPEN_IN_INCOGNITO = 1; // 0x1
+    field @Deprecated public static final int ITEM_OPEN_IN_NEW_TAB = 0; // 0x0
+    field @Deprecated public static final int ITEM_SHARE = 4; // 0x4
+    field @Deprecated public static final String KEY_ACTION = "androidx.browser.browseractions.ACTION";
+    field @Deprecated public static final String KEY_ICON_ID = "androidx.browser.browseractions.ICON_ID";
+    field @Deprecated public static final String KEY_TITLE = "androidx.browser.browseractions.TITLE";
+    field @Deprecated public static final int MAX_CUSTOM_ITEMS = 5; // 0x5
+    field @Deprecated public static final int URL_TYPE_AUDIO = 3; // 0x3
+    field @Deprecated public static final int URL_TYPE_FILE = 4; // 0x4
+    field @Deprecated public static final int URL_TYPE_IMAGE = 1; // 0x1
+    field @Deprecated public static final int URL_TYPE_NONE = 0; // 0x0
+    field @Deprecated public static final int URL_TYPE_PLUGIN = 5; // 0x5
+    field @Deprecated public static final int URL_TYPE_VIDEO = 2; // 0x2
+  }
+
+  @Deprecated public static final class BrowserActionsIntent.Builder {
+    ctor @Deprecated public BrowserActionsIntent.Builder(android.content.Context!, android.net.Uri!);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent! build();
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setCustomItems(java.util.ArrayList<androidx.browser.browseractions.BrowserActionItem!>!);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setCustomItems(androidx.browser.browseractions.BrowserActionItem!...);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setOnItemSelectedAction(android.app.PendingIntent!);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setUrlType(int);
+  }
+
+}
+
+package androidx.browser.customtabs {
+
+  public final class CustomTabColorSchemeParams {
+    field @ColorInt public final Integer? navigationBarColor;
+    field @ColorInt public final Integer? secondaryToolbarColor;
+    field @ColorInt public final Integer? toolbarColor;
+  }
+
+  public static final class CustomTabColorSchemeParams.Builder {
+    ctor public CustomTabColorSchemeParams.Builder();
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams build();
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams.Builder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams.Builder setSecondaryToolbarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams.Builder setToolbarColor(@ColorInt int);
+  }
+
+  public class CustomTabsCallback {
+    ctor public CustomTabsCallback();
+    method public void extraCallback(String!, android.os.Bundle!);
+    method public void onMessageChannelReady(android.os.Bundle!);
+    method public void onNavigationEvent(int, android.os.Bundle!);
+    method public void onPostMessage(String!, android.os.Bundle!);
+    method public void onRelationshipValidationResult(@androidx.browser.customtabs.CustomTabsService.Relation int, android.net.Uri!, boolean, android.os.Bundle!);
+    field public static final int NAVIGATION_ABORTED = 4; // 0x4
+    field public static final int NAVIGATION_FAILED = 3; // 0x3
+    field public static final int NAVIGATION_FINISHED = 2; // 0x2
+    field public static final int NAVIGATION_STARTED = 1; // 0x1
+    field public static final int TAB_HIDDEN = 6; // 0x6
+    field public static final int TAB_SHOWN = 5; // 0x5
+  }
+
+  public class CustomTabsClient {
+    method public static boolean bindCustomTabsService(android.content.Context, String?, androidx.browser.customtabs.CustomTabsServiceConnection);
+    method public static boolean connectAndInitialize(android.content.Context, String);
+    method public android.os.Bundle? extraCommand(String, android.os.Bundle?);
+    method public static String? getPackageName(android.content.Context, java.util.List<java.lang.String!>?);
+    method public static String? getPackageName(android.content.Context, java.util.List<java.lang.String!>?, boolean);
+    method public androidx.browser.customtabs.CustomTabsSession? newSession(androidx.browser.customtabs.CustomTabsCallback?);
+    method public androidx.browser.customtabs.CustomTabsSession? newSession(androidx.browser.customtabs.CustomTabsCallback?, int);
+    method public boolean warmup(long);
+  }
+
+  public final class CustomTabsIntent {
+    method public static androidx.browser.customtabs.CustomTabColorSchemeParams getColorSchemeParams(android.content.Intent, int);
+    method public static int getMaxToolbarItems();
+    method public void launchUrl(android.content.Context!, android.net.Uri!);
+    method public static android.content.Intent! setAlwaysUseBrowserUI(android.content.Intent!);
+    method public static boolean shouldAlwaysUseBrowserUI(android.content.Intent!);
+    field public static final int COLOR_SCHEME_DARK = 2; // 0x2
+    field public static final int COLOR_SCHEME_LIGHT = 1; // 0x1
+    field public static final int COLOR_SCHEME_SYSTEM = 0; // 0x0
+    field public static final String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
+    field public static final String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
+    field public static final String EXTRA_COLOR_SCHEME = "androidx.browser.customtabs.extra.COLOR_SCHEME";
+    field public static final String EXTRA_COLOR_SCHEME_PARAMS = "androidx.browser.customtabs.extra.COLOR_SCHEME_PARAMS";
+    field public static final String EXTRA_DEFAULT_SHARE_MENU_ITEM = "android.support.customtabs.extra.SHARE_MENU_ITEM";
+    field public static final String EXTRA_ENABLE_INSTANT_APPS = "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
+    field public static final String EXTRA_ENABLE_URLBAR_HIDING = "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
+    field public static final String EXTRA_EXIT_ANIMATION_BUNDLE = "android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
+    field public static final String EXTRA_MENU_ITEMS = "android.support.customtabs.extra.MENU_ITEMS";
+    field public static final String EXTRA_NAVIGATION_BAR_COLOR = "androidx.browser.customtabs.extra.NAVIGATION_BAR_COLOR";
+    field public static final String EXTRA_REMOTEVIEWS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS";
+    field public static final String EXTRA_REMOTEVIEWS_CLICKED_ID = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID";
+    field public static final String EXTRA_REMOTEVIEWS_PENDINGINTENT = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_PENDINGINTENT";
+    field public static final String EXTRA_REMOTEVIEWS_VIEW_IDS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_VIEW_IDS";
+    field public static final String EXTRA_SECONDARY_TOOLBAR_COLOR = "android.support.customtabs.extra.SECONDARY_TOOLBAR_COLOR";
+    field public static final String EXTRA_SESSION = "android.support.customtabs.extra.SESSION";
+    field public static final String EXTRA_TINT_ACTION_BUTTON = "android.support.customtabs.extra.TINT_ACTION_BUTTON";
+    field public static final String EXTRA_TITLE_VISIBILITY_STATE = "android.support.customtabs.extra.TITLE_VISIBILITY";
+    field public static final String EXTRA_TOOLBAR_COLOR = "android.support.customtabs.extra.TOOLBAR_COLOR";
+    field public static final String EXTRA_TOOLBAR_ITEMS = "android.support.customtabs.extra.TOOLBAR_ITEMS";
+    field public static final String KEY_DESCRIPTION = "android.support.customtabs.customaction.DESCRIPTION";
+    field public static final String KEY_ICON = "android.support.customtabs.customaction.ICON";
+    field public static final String KEY_ID = "android.support.customtabs.customaction.ID";
+    field public static final String KEY_MENU_ITEM_TITLE = "android.support.customtabs.customaction.MENU_ITEM_TITLE";
+    field public static final String KEY_PENDING_INTENT = "android.support.customtabs.customaction.PENDING_INTENT";
+    field public static final int NO_TITLE = 0; // 0x0
+    field public static final int SHOW_PAGE_TITLE = 1; // 0x1
+    field public static final int TOOLBAR_ACTION_BUTTON_ID = 0; // 0x0
+    field public final android.content.Intent intent;
+    field public final android.os.Bundle? startAnimationBundle;
+  }
+
+  public static final class CustomTabsIntent.Builder {
+    ctor public CustomTabsIntent.Builder();
+    ctor public CustomTabsIntent.Builder(androidx.browser.customtabs.CustomTabsSession?);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder addDefaultShareMenuItem();
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder addMenuItem(String, android.app.PendingIntent);
+    method @Deprecated public androidx.browser.customtabs.CustomTabsIntent.Builder addToolbarItem(int, android.graphics.Bitmap, String, android.app.PendingIntent!) throws java.lang.IllegalStateException;
+    method public androidx.browser.customtabs.CustomTabsIntent build();
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder enableUrlBarHiding();
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, String, android.app.PendingIntent, boolean);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, String, android.app.PendingIntent);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setCloseButtonIcon(android.graphics.Bitmap);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setColorScheme(int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setColorSchemeParams(int, androidx.browser.customtabs.CustomTabColorSchemeParams);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setExitAnimations(android.content.Context, @AnimRes int, @AnimRes int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setInstantAppsEnabled(boolean);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSecondaryToolbarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSecondaryToolbarViews(android.widget.RemoteViews, int[]?, android.app.PendingIntent?);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSession(androidx.browser.customtabs.CustomTabsSession);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setShowTitle(boolean);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setStartAnimations(android.content.Context, @AnimRes int, @AnimRes int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setToolbarColor(@ColorInt int);
+  }
+
+  public abstract class CustomTabsService extends android.app.Service {
+    ctor public CustomTabsService();
+    method protected boolean cleanUpSession(androidx.browser.customtabs.CustomTabsSessionToken!);
+    method protected abstract android.os.Bundle! extraCommand(String!, android.os.Bundle!);
+    method protected abstract boolean mayLaunchUrl(androidx.browser.customtabs.CustomTabsSessionToken!, android.net.Uri!, android.os.Bundle!, java.util.List<android.os.Bundle!>!);
+    method protected abstract boolean newSession(androidx.browser.customtabs.CustomTabsSessionToken!);
+    method public android.os.IBinder! onBind(android.content.Intent!);
+    method @androidx.browser.customtabs.CustomTabsService.Result protected abstract int postMessage(androidx.browser.customtabs.CustomTabsSessionToken!, String!, android.os.Bundle!);
+    method protected abstract boolean receiveFile(androidx.browser.customtabs.CustomTabsSessionToken, android.net.Uri, int, android.os.Bundle?);
+    method protected abstract boolean requestPostMessageChannel(androidx.browser.customtabs.CustomTabsSessionToken!, android.net.Uri!);
+    method protected abstract boolean updateVisuals(androidx.browser.customtabs.CustomTabsSessionToken!, android.os.Bundle!);
+    method protected abstract boolean validateRelationship(androidx.browser.customtabs.CustomTabsSessionToken!, @androidx.browser.customtabs.CustomTabsService.Relation int, android.net.Uri!, android.os.Bundle!);
+    method protected abstract boolean warmup(long);
+    field public static final String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";
+    field public static final String CATEGORY_COLOR_SCHEME_CUSTOMIZATION = "androidx.browser.customtabs.category.ColorSchemeCustomization";
+    field public static final String CATEGORY_NAVBAR_COLOR_CUSTOMIZATION = "androidx.browser.customtabs.category.NavBarColorCustomization";
+    field public static final int FILE_PURPOSE_TRUSTED_WEB_ACTIVITY_SPLASH_IMAGE = 1; // 0x1
+    field public static final String KEY_URL = "android.support.customtabs.otherurls.URL";
+    field public static final int RELATION_HANDLE_ALL_URLS = 2; // 0x2
+    field public static final int RELATION_USE_AS_ORIGIN = 1; // 0x1
+    field public static final int RESULT_FAILURE_DISALLOWED = -1; // 0xffffffff
+    field public static final int RESULT_FAILURE_MESSAGING_ERROR = -3; // 0xfffffffd
+    field public static final int RESULT_FAILURE_REMOTE_ERROR = -2; // 0xfffffffe
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+    field public static final String TRUSTED_WEB_ACTIVITY_CATEGORY = "androidx.browser.trusted.category.TrustedWebActivities";
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({androidx.browser.customtabs.CustomTabsService.RELATION_USE_AS_ORIGIN, androidx.browser.customtabs.CustomTabsService.RELATION_HANDLE_ALL_URLS}) public static @interface CustomTabsService.Relation {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({androidx.browser.customtabs.CustomTabsService.RESULT_SUCCESS, androidx.browser.customtabs.CustomTabsService.RESULT_FAILURE_DISALLOWED, androidx.browser.customtabs.CustomTabsService.RESULT_FAILURE_REMOTE_ERROR, androidx.browser.customtabs.CustomTabsService.RESULT_FAILURE_MESSAGING_ERROR}) public static @interface CustomTabsService.Result {
+  }
+
+  public abstract class CustomTabsServiceConnection implements android.content.ServiceConnection {
+    ctor public CustomTabsServiceConnection();
+    method public abstract void onCustomTabsServiceConnected(android.content.ComponentName!, androidx.browser.customtabs.CustomTabsClient!);
+    method public final void onServiceConnected(android.content.ComponentName!, android.os.IBinder!);
+  }
+
+  public final class CustomTabsSession {
+    method @VisibleForTesting public static androidx.browser.customtabs.CustomTabsSession createMockSessionForTesting(android.content.ComponentName);
+    method public boolean mayLaunchUrl(android.net.Uri!, android.os.Bundle!, java.util.List<android.os.Bundle!>!);
+    method @androidx.browser.customtabs.CustomTabsService.Result public int postMessage(String!, android.os.Bundle!);
+    method public boolean receiveFile(android.net.Uri, int, android.os.Bundle?);
+    method public boolean requestPostMessageChannel(android.net.Uri!);
+    method public boolean setActionButton(android.graphics.Bitmap, String);
+    method public boolean setSecondaryToolbarViews(android.widget.RemoteViews?, int[]?, android.app.PendingIntent?);
+    method @Deprecated public boolean setToolbarItem(int, android.graphics.Bitmap, String);
+    method public boolean validateRelationship(@androidx.browser.customtabs.CustomTabsService.Relation int, android.net.Uri, android.os.Bundle?);
+  }
+
+  public class CustomTabsSessionToken {
+    method public static androidx.browser.customtabs.CustomTabsSessionToken createMockSessionTokenForTesting();
+    method public androidx.browser.customtabs.CustomTabsCallback? getCallback();
+    method public static androidx.browser.customtabs.CustomTabsSessionToken? getSessionTokenFromIntent(android.content.Intent);
+    method public boolean isAssociatedWith(androidx.browser.customtabs.CustomTabsSession);
+  }
+
+  public class PostMessageService extends android.app.Service {
+    ctor public PostMessageService();
+    method public android.os.IBinder! onBind(android.content.Intent!);
+  }
+
+  public abstract class PostMessageServiceConnection implements android.content.ServiceConnection {
+    ctor public PostMessageServiceConnection(androidx.browser.customtabs.CustomTabsSessionToken!);
+    method public boolean bindSessionToPostMessageService(android.content.Context!, String!);
+    method public final boolean notifyMessageChannelReady(android.os.Bundle!);
+    method public void onPostMessageServiceConnected();
+    method public void onPostMessageServiceDisconnected();
+    method public final void onServiceConnected(android.content.ComponentName!, android.os.IBinder!);
+    method public final void onServiceDisconnected(android.content.ComponentName!);
+    method public final boolean postMessage(String!, android.os.Bundle!);
+    method public void unbindFromContext(android.content.Context!);
+  }
+
+  public class TrustedWebUtils {
+    method @Deprecated public static void launchAsTrustedWebActivity(android.content.Context, androidx.browser.customtabs.CustomTabsIntent, android.net.Uri);
+    method public static boolean splashScreensAreSupported(android.content.Context, String, String);
+    method @WorkerThread public static boolean transferSplashImage(android.content.Context, java.io.File, String, String, androidx.browser.customtabs.CustomTabsSession);
+    field public static final String EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY = "android.support.customtabs.extra.LAUNCH_AS_TRUSTED_WEB_ACTIVITY";
+  }
+
+}
+
+package androidx.browser.trusted {
+
+  public class TrustedWebActivityIntentBuilder {
+    ctor public TrustedWebActivityIntentBuilder(android.net.Uri);
+    method public android.content.Intent build(androidx.browser.customtabs.CustomTabsSession);
+    method public androidx.browser.customtabs.CustomTabsIntent buildCustomTabsIntent();
+    method public android.net.Uri getUrl();
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setAdditionalTrustedOrigins(java.util.List<java.lang.String!>);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setColorScheme(int);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setColorSchemeParams(int, androidx.browser.customtabs.CustomTabColorSchemeParams);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setSplashScreenParams(android.os.Bundle);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setToolbarColor(@ColorInt int);
+    field public static final String EXTRA_ADDITIONAL_TRUSTED_ORIGINS = "android.support.customtabs.extra.ADDITIONAL_TRUSTED_ORIGINS";
+    field public static final String EXTRA_SPLASH_SCREEN_PARAMS = "androidx.browser.trusted.EXTRA_SPLASH_SCREEN_PARAMS";
+  }
+
+  public class TrustedWebActivityService extends android.app.Service {
+    ctor public TrustedWebActivityService();
+    method public boolean areNotificationsEnabled(String);
+    method public void cancelNotification(String, int);
+    method public android.os.Bundle getSmallIconBitmap();
+    method public int getSmallIconId();
+    method public boolean notifyNotificationWithChannel(String, int, android.app.Notification, String);
+    method public final android.os.IBinder? onBind(android.content.Intent?);
+    method public final boolean onUnbind(android.content.Intent?);
+    method public static final void setVerifiedProvider(android.content.Context, String?);
+    field public static final String ACTION_TRUSTED_WEB_ACTIVITY_SERVICE = "android.support.customtabs.trusted.TRUSTED_WEB_ACTIVITY_SERVICE";
+    field public static final String KEY_SMALL_ICON_BITMAP = "android.support.customtabs.trusted.SMALL_ICON_BITMAP";
+    field public static final String META_DATA_NAME_SMALL_ICON = "android.support.customtabs.trusted.SMALL_ICON";
+    field public static final int SMALL_ICON_NOT_SET = -1; // 0xffffffff
+  }
+
+  public class TrustedWebActivityServiceConnectionManager {
+    ctor public TrustedWebActivityServiceConnectionManager(android.content.Context);
+    method @MainThread public boolean execute(android.net.Uri, String, androidx.browser.trusted.TrustedWebActivityServiceConnectionManager.ExecutionCallback);
+    method public static java.util.Set<java.lang.String!> getVerifiedPackages(android.content.Context, String);
+    method public static void registerClient(android.content.Context, String, String);
+    method @MainThread public boolean serviceExistsForScope(android.net.Uri, String);
+  }
+
+  public static interface TrustedWebActivityServiceConnectionManager.ExecutionCallback {
+    method public void onConnected(androidx.browser.trusted.TrustedWebActivityServiceWrapper?) throws android.os.RemoteException;
+  }
+
+  public class TrustedWebActivityServiceWrapper {
+    method public boolean areNotificationsEnabled(String);
+    method public void cancel(String, int);
+    method public android.content.ComponentName getComponentName();
+    method public android.graphics.Bitmap? getSmallIconBitmap();
+    method public int getSmallIconId();
+    method public boolean notify(String, int, android.app.Notification, String);
+  }
+
+}
+
+package androidx.browser.trusted.splashscreens {
+
+  public final class SplashScreenParamKey {
+    field public static final String BACKGROUND_COLOR = "androidx.browser.trusted.trusted.KEY_SPLASH_SCREEN_BACKGROUND_COLOR";
+    field public static final String FADE_OUT_DURATION_MS = "androidx.browser.trusted.KEY_SPLASH_SCREEN_FADE_OUT_DURATION";
+    field public static final String IMAGE_TRANSFORMATION_MATRIX = "androidx.browser.trusted.KEY_SPLASH_SCREEN_TRANSFORMATION_MATRIX";
+    field public static final String SCALE_TYPE = "androidx.browser.trusted.KEY_SPLASH_SCREEN_SCALE_TYPE";
+    field public static final String VERSION = "androidx.browser.trusted.KEY_SPLASH_SCREEN_VERSION";
+  }
+
+  public final class SplashScreenVersion {
+    field public static final String V1 = "androidx.browser.trusted.category.TrustedWebActivitySplashScreensV1";
+  }
+
+}
+
diff --git a/browser/api/current.txt b/browser/api/current.txt
index 4e2c30d..798e756 100644
--- a/browser/api/current.txt
+++ b/browser/api/current.txt
@@ -152,6 +152,7 @@
     method public androidx.browser.customtabs.CustomTabsIntent.Builder setNavigationBarColor(@ColorInt int);
     method public androidx.browser.customtabs.CustomTabsIntent.Builder setSecondaryToolbarColor(@ColorInt int);
     method public androidx.browser.customtabs.CustomTabsIntent.Builder setSecondaryToolbarViews(android.widget.RemoteViews, int[]?, android.app.PendingIntent?);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSession(androidx.browser.customtabs.CustomTabsSession);
     method public androidx.browser.customtabs.CustomTabsIntent.Builder setShowTitle(boolean);
     method public androidx.browser.customtabs.CustomTabsIntent.Builder setStartAnimations(android.content.Context, @AnimRes int, @AnimRes int);
     method public androidx.browser.customtabs.CustomTabsIntent.Builder setToolbarColor(@ColorInt int);
@@ -243,16 +244,58 @@
 
 package androidx.browser.trusted {
 
-  public class TrustedWebActivityBuilder {
-    ctor public TrustedWebActivityBuilder(android.content.Context, android.net.Uri);
+  public class TrustedWebActivityIntentBuilder {
+    ctor public TrustedWebActivityIntentBuilder(android.net.Uri);
+    method public android.content.Intent build(androidx.browser.customtabs.CustomTabsSession);
+    method public androidx.browser.customtabs.CustomTabsIntent buildCustomTabsIntent();
     method public android.net.Uri getUrl();
-    method public void launchActivity(androidx.browser.customtabs.CustomTabsSession);
-    method public androidx.browser.trusted.TrustedWebActivityBuilder setAdditionalTrustedOrigins(java.util.List<java.lang.String!>);
-    method public androidx.browser.trusted.TrustedWebActivityBuilder setSplashScreenParams(android.os.Bundle);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setAdditionalTrustedOrigins(java.util.List<java.lang.String!>);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setColorScheme(int);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setColorSchemeParams(int, androidx.browser.customtabs.CustomTabColorSchemeParams);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setSplashScreenParams(android.os.Bundle);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setToolbarColor(@ColorInt int);
     field public static final String EXTRA_ADDITIONAL_TRUSTED_ORIGINS = "android.support.customtabs.extra.ADDITIONAL_TRUSTED_ORIGINS";
     field public static final String EXTRA_SPLASH_SCREEN_PARAMS = "androidx.browser.trusted.EXTRA_SPLASH_SCREEN_PARAMS";
   }
 
+  public class TrustedWebActivityService extends android.app.Service {
+    ctor public TrustedWebActivityService();
+    method public boolean areNotificationsEnabled(String);
+    method public void cancelNotification(String, int);
+    method public android.os.Bundle getSmallIconBitmap();
+    method public int getSmallIconId();
+    method public boolean notifyNotificationWithChannel(String, int, android.app.Notification, String);
+    method public final android.os.IBinder? onBind(android.content.Intent?);
+    method public final boolean onUnbind(android.content.Intent?);
+    method public static final void setVerifiedProvider(android.content.Context, String?);
+    field public static final String ACTION_TRUSTED_WEB_ACTIVITY_SERVICE = "android.support.customtabs.trusted.TRUSTED_WEB_ACTIVITY_SERVICE";
+    field public static final String KEY_SMALL_ICON_BITMAP = "android.support.customtabs.trusted.SMALL_ICON_BITMAP";
+    field public static final String META_DATA_NAME_SMALL_ICON = "android.support.customtabs.trusted.SMALL_ICON";
+    field public static final int SMALL_ICON_NOT_SET = -1; // 0xffffffff
+  }
+
+  public class TrustedWebActivityServiceConnectionManager {
+    ctor public TrustedWebActivityServiceConnectionManager(android.content.Context);
+    method @MainThread public boolean execute(android.net.Uri, String, androidx.browser.trusted.TrustedWebActivityServiceConnectionManager.ExecutionCallback);
+    method public static java.util.Set<java.lang.String!> getVerifiedPackages(android.content.Context, String);
+    method public static void registerClient(android.content.Context, String, String);
+    method @MainThread public boolean serviceExistsForScope(android.net.Uri, String);
+  }
+
+  public static interface TrustedWebActivityServiceConnectionManager.ExecutionCallback {
+    method public void onConnected(androidx.browser.trusted.TrustedWebActivityServiceWrapper?) throws android.os.RemoteException;
+  }
+
+  public class TrustedWebActivityServiceWrapper {
+    method public boolean areNotificationsEnabled(String);
+    method public void cancel(String, int);
+    method public android.content.ComponentName getComponentName();
+    method public android.graphics.Bitmap? getSmallIconBitmap();
+    method public int getSmallIconId();
+    method public boolean notify(String, int, android.app.Notification, String);
+  }
+
 }
 
 package androidx.browser.trusted.splashscreens {
diff --git a/benchmark/api/res-1.0.0-alpha01.txt b/browser/api/res-1.2.0-alpha06.txt
similarity index 100%
copy from benchmark/api/res-1.0.0-alpha01.txt
copy to browser/api/res-1.2.0-alpha06.txt
diff --git a/benchmark/api/res-1.0.0-alpha01.txt b/browser/api/res-1.2.0-alpha07.txt
similarity index 100%
copy from benchmark/api/res-1.0.0-alpha01.txt
copy to browser/api/res-1.2.0-alpha07.txt
diff --git a/browser/api/restricted_1.2.0-alpha06.txt b/browser/api/restricted_1.2.0-alpha06.txt
new file mode 100644
index 0000000..8f41848
--- /dev/null
+++ b/browser/api/restricted_1.2.0-alpha06.txt
@@ -0,0 +1,300 @@
+// Signature format: 3.0
+package androidx.browser.browseractions {
+
+  @Deprecated public class BrowserActionItem {
+    ctor @Deprecated public BrowserActionItem(String, android.app.PendingIntent, @DrawableRes int);
+    ctor @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public BrowserActionItem(String, android.app.PendingIntent, android.net.Uri);
+    ctor @Deprecated public BrowserActionItem(String, android.app.PendingIntent);
+    method @Deprecated public android.app.PendingIntent getAction();
+    method @Deprecated public int getIconId();
+    method @Deprecated public String getTitle();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class BrowserActionsFallbackMenuView extends android.widget.LinearLayout {
+    ctor public BrowserActionsFallbackMenuView(android.content.Context!, android.util.AttributeSet!);
+  }
+
+  @Deprecated public class BrowserActionsIntent {
+    method @Deprecated public static String? getCreatorPackageName(android.content.Intent);
+    method @Deprecated public android.content.Intent getIntent();
+    method @Deprecated public static String? getUntrustedCreatorPackageName(android.content.Intent);
+    method @Deprecated public static void launchIntent(android.content.Context!, android.content.Intent!);
+    method @Deprecated public static void openBrowserAction(android.content.Context!, android.net.Uri!);
+    method @Deprecated public static void openBrowserAction(android.content.Context!, android.net.Uri!, int, java.util.ArrayList<androidx.browser.browseractions.BrowserActionItem!>!, android.app.PendingIntent!);
+    method @Deprecated public static java.util.List<androidx.browser.browseractions.BrowserActionItem!>! parseBrowserActionItems(java.util.ArrayList<android.os.Bundle!>!);
+    field @Deprecated public static final String ACTION_BROWSER_ACTIONS_OPEN = "androidx.browser.browseractions.browser_action_open";
+    field @Deprecated public static final String EXTRA_APP_ID = "androidx.browser.browseractions.APP_ID";
+    field @Deprecated public static final String EXTRA_MENU_ITEMS = "androidx.browser.browseractions.extra.MENU_ITEMS";
+    field @Deprecated public static final String EXTRA_SELECTED_ACTION_PENDING_INTENT = "androidx.browser.browseractions.extra.SELECTED_ACTION_PENDING_INTENT";
+    field @Deprecated public static final String EXTRA_TYPE = "androidx.browser.browseractions.extra.TYPE";
+    field @Deprecated public static final int ITEM_COPY = 3; // 0x3
+    field @Deprecated public static final int ITEM_DOWNLOAD = 2; // 0x2
+    field @Deprecated public static final int ITEM_INVALID_ITEM = -1; // 0xffffffff
+    field @Deprecated public static final int ITEM_OPEN_IN_INCOGNITO = 1; // 0x1
+    field @Deprecated public static final int ITEM_OPEN_IN_NEW_TAB = 0; // 0x0
+    field @Deprecated public static final int ITEM_SHARE = 4; // 0x4
+    field @Deprecated public static final String KEY_ACTION = "androidx.browser.browseractions.ACTION";
+    field @Deprecated public static final String KEY_ICON_ID = "androidx.browser.browseractions.ICON_ID";
+    field @Deprecated public static final String KEY_TITLE = "androidx.browser.browseractions.TITLE";
+    field @Deprecated public static final int MAX_CUSTOM_ITEMS = 5; // 0x5
+    field @Deprecated public static final int URL_TYPE_AUDIO = 3; // 0x3
+    field @Deprecated public static final int URL_TYPE_FILE = 4; // 0x4
+    field @Deprecated public static final int URL_TYPE_IMAGE = 1; // 0x1
+    field @Deprecated public static final int URL_TYPE_NONE = 0; // 0x0
+    field @Deprecated public static final int URL_TYPE_PLUGIN = 5; // 0x5
+    field @Deprecated public static final int URL_TYPE_VIDEO = 2; // 0x2
+  }
+
+  @Deprecated @IntDef({androidx.browser.browseractions.BrowserActionsIntent.ITEM_INVALID_ITEM, androidx.browser.browseractions.BrowserActionsIntent.ITEM_OPEN_IN_NEW_TAB, androidx.browser.browseractions.BrowserActionsIntent.ITEM_OPEN_IN_INCOGNITO, androidx.browser.browseractions.BrowserActionsIntent.ITEM_DOWNLOAD, androidx.browser.browseractions.BrowserActionsIntent.ITEM_COPY, androidx.browser.browseractions.BrowserActionsIntent.ITEM_SHARE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface BrowserActionsIntent.BrowserActionsItemId {
+  }
+
+  @Deprecated @IntDef({androidx.browser.browseractions.BrowserActionsIntent.URL_TYPE_NONE, androidx.browser.browseractions.BrowserActionsIntent.URL_TYPE_IMAGE, androidx.browser.browseractions.BrowserActionsIntent.URL_TYPE_VIDEO, androidx.browser.browseractions.BrowserActionsIntent.URL_TYPE_AUDIO, androidx.browser.browseractions.BrowserActionsIntent.URL_TYPE_FILE, androidx.browser.browseractions.BrowserActionsIntent.URL_TYPE_PLUGIN}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface BrowserActionsIntent.BrowserActionsUrlType {
+  }
+
+  @Deprecated public static final class BrowserActionsIntent.Builder {
+    ctor @Deprecated public BrowserActionsIntent.Builder(android.content.Context!, android.net.Uri!);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent! build();
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setCustomItems(java.util.ArrayList<androidx.browser.browseractions.BrowserActionItem!>!);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setCustomItems(androidx.browser.browseractions.BrowserActionItem!...);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setOnItemSelectedAction(android.app.PendingIntent!);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setUrlType(@androidx.browser.browseractions.BrowserActionsIntent.BrowserActionsUrlType int);
+  }
+
+
+}
+
+package androidx.browser.customtabs {
+
+  public final class CustomTabColorSchemeParams {
+    field @ColorInt public final Integer? navigationBarColor;
+    field @ColorInt public final Integer? secondaryToolbarColor;
+    field @ColorInt public final Integer? toolbarColor;
+  }
+
+  public static final class CustomTabColorSchemeParams.Builder {
+    ctor public CustomTabColorSchemeParams.Builder();
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams build();
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams.Builder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams.Builder setSecondaryToolbarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams.Builder setToolbarColor(@ColorInt int);
+  }
+
+  public class CustomTabsCallback {
+    ctor public CustomTabsCallback();
+    method public void extraCallback(String!, android.os.Bundle!);
+    method public void onMessageChannelReady(android.os.Bundle!);
+    method public void onNavigationEvent(int, android.os.Bundle!);
+    method public void onPostMessage(String!, android.os.Bundle!);
+    method public void onRelationshipValidationResult(@androidx.browser.customtabs.CustomTabsService.Relation int, android.net.Uri!, boolean, android.os.Bundle!);
+    field public static final int NAVIGATION_ABORTED = 4; // 0x4
+    field public static final int NAVIGATION_FAILED = 3; // 0x3
+    field public static final int NAVIGATION_FINISHED = 2; // 0x2
+    field public static final int NAVIGATION_STARTED = 1; // 0x1
+    field public static final int TAB_HIDDEN = 6; // 0x6
+    field public static final int TAB_SHOWN = 5; // 0x5
+  }
+
+  public class CustomTabsClient {
+    method public static boolean bindCustomTabsService(android.content.Context, String?, androidx.browser.customtabs.CustomTabsServiceConnection);
+    method public static boolean connectAndInitialize(android.content.Context, String);
+    method public android.os.Bundle? extraCommand(String, android.os.Bundle?);
+    method public static String? getPackageName(android.content.Context, java.util.List<java.lang.String!>?);
+    method public static String? getPackageName(android.content.Context, java.util.List<java.lang.String!>?, boolean);
+    method public androidx.browser.customtabs.CustomTabsSession? newSession(androidx.browser.customtabs.CustomTabsCallback?);
+    method public androidx.browser.customtabs.CustomTabsSession? newSession(androidx.browser.customtabs.CustomTabsCallback?, int);
+    method public boolean warmup(long);
+  }
+
+  public final class CustomTabsIntent {
+    method public static androidx.browser.customtabs.CustomTabColorSchemeParams getColorSchemeParams(android.content.Intent, @androidx.browser.customtabs.CustomTabsIntent.ColorScheme int);
+    method public static int getMaxToolbarItems();
+    method public void launchUrl(android.content.Context!, android.net.Uri!);
+    method public static android.content.Intent! setAlwaysUseBrowserUI(android.content.Intent!);
+    method public static boolean shouldAlwaysUseBrowserUI(android.content.Intent!);
+    field public static final int COLOR_SCHEME_DARK = 2; // 0x2
+    field public static final int COLOR_SCHEME_LIGHT = 1; // 0x1
+    field public static final int COLOR_SCHEME_SYSTEM = 0; // 0x0
+    field public static final String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
+    field public static final String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
+    field public static final String EXTRA_COLOR_SCHEME = "androidx.browser.customtabs.extra.COLOR_SCHEME";
+    field public static final String EXTRA_COLOR_SCHEME_PARAMS = "androidx.browser.customtabs.extra.COLOR_SCHEME_PARAMS";
+    field public static final String EXTRA_DEFAULT_SHARE_MENU_ITEM = "android.support.customtabs.extra.SHARE_MENU_ITEM";
+    field public static final String EXTRA_ENABLE_INSTANT_APPS = "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
+    field public static final String EXTRA_ENABLE_URLBAR_HIDING = "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
+    field public static final String EXTRA_EXIT_ANIMATION_BUNDLE = "android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
+    field public static final String EXTRA_MENU_ITEMS = "android.support.customtabs.extra.MENU_ITEMS";
+    field public static final String EXTRA_NAVIGATION_BAR_COLOR = "androidx.browser.customtabs.extra.NAVIGATION_BAR_COLOR";
+    field public static final String EXTRA_REMOTEVIEWS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS";
+    field public static final String EXTRA_REMOTEVIEWS_CLICKED_ID = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID";
+    field public static final String EXTRA_REMOTEVIEWS_PENDINGINTENT = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_PENDINGINTENT";
+    field public static final String EXTRA_REMOTEVIEWS_VIEW_IDS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_VIEW_IDS";
+    field public static final String EXTRA_SECONDARY_TOOLBAR_COLOR = "android.support.customtabs.extra.SECONDARY_TOOLBAR_COLOR";
+    field public static final String EXTRA_SESSION = "android.support.customtabs.extra.SESSION";
+    field public static final String EXTRA_TINT_ACTION_BUTTON = "android.support.customtabs.extra.TINT_ACTION_BUTTON";
+    field public static final String EXTRA_TITLE_VISIBILITY_STATE = "android.support.customtabs.extra.TITLE_VISIBILITY";
+    field public static final String EXTRA_TOOLBAR_COLOR = "android.support.customtabs.extra.TOOLBAR_COLOR";
+    field public static final String EXTRA_TOOLBAR_ITEMS = "android.support.customtabs.extra.TOOLBAR_ITEMS";
+    field public static final String KEY_DESCRIPTION = "android.support.customtabs.customaction.DESCRIPTION";
+    field public static final String KEY_ICON = "android.support.customtabs.customaction.ICON";
+    field public static final String KEY_ID = "android.support.customtabs.customaction.ID";
+    field public static final String KEY_MENU_ITEM_TITLE = "android.support.customtabs.customaction.MENU_ITEM_TITLE";
+    field public static final String KEY_PENDING_INTENT = "android.support.customtabs.customaction.PENDING_INTENT";
+    field public static final int NO_TITLE = 0; // 0x0
+    field public static final int SHOW_PAGE_TITLE = 1; // 0x1
+    field public static final int TOOLBAR_ACTION_BUTTON_ID = 0; // 0x0
+    field public final android.content.Intent intent;
+    field public final android.os.Bundle? startAnimationBundle;
+  }
+
+  public static final class CustomTabsIntent.Builder {
+    ctor public CustomTabsIntent.Builder();
+    ctor public CustomTabsIntent.Builder(androidx.browser.customtabs.CustomTabsSession?);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder addDefaultShareMenuItem();
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder addMenuItem(String, android.app.PendingIntent);
+    method @Deprecated public androidx.browser.customtabs.CustomTabsIntent.Builder addToolbarItem(int, android.graphics.Bitmap, String, android.app.PendingIntent!) throws java.lang.IllegalStateException;
+    method public androidx.browser.customtabs.CustomTabsIntent build();
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder enableUrlBarHiding();
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, String, android.app.PendingIntent, boolean);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, String, android.app.PendingIntent);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setCloseButtonIcon(android.graphics.Bitmap);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setColorScheme(@androidx.browser.customtabs.CustomTabsIntent.ColorScheme int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setColorSchemeParams(@androidx.browser.customtabs.CustomTabsIntent.ColorScheme int, androidx.browser.customtabs.CustomTabColorSchemeParams);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setExitAnimations(android.content.Context, @AnimRes int, @AnimRes int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setInstantAppsEnabled(boolean);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSecondaryToolbarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSecondaryToolbarViews(android.widget.RemoteViews, int[]?, android.app.PendingIntent?);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSession(androidx.browser.customtabs.CustomTabsSession);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setShowTitle(boolean);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setStartAnimations(android.content.Context, @AnimRes int, @AnimRes int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setToolbarColor(@ColorInt int);
+  }
+
+
+  public abstract class CustomTabsService extends android.app.Service {
+    ctor public CustomTabsService();
+    method protected boolean cleanUpSession(androidx.browser.customtabs.CustomTabsSessionToken!);
+    method protected abstract android.os.Bundle! extraCommand(String!, android.os.Bundle!);
+    method protected abstract boolean mayLaunchUrl(androidx.browser.customtabs.CustomTabsSessionToken!, android.net.Uri!, android.os.Bundle!, java.util.List<android.os.Bundle!>!);
+    method protected abstract boolean newSession(androidx.browser.customtabs.CustomTabsSessionToken!);
+    method public android.os.IBinder! onBind(android.content.Intent!);
+    method @androidx.browser.customtabs.CustomTabsService.Result protected abstract int postMessage(androidx.browser.customtabs.CustomTabsSessionToken!, String!, android.os.Bundle!);
+    method protected abstract boolean receiveFile(androidx.browser.customtabs.CustomTabsSessionToken, android.net.Uri, @androidx.browser.customtabs.CustomTabsService.FilePurpose int, android.os.Bundle?);
+    method protected abstract boolean requestPostMessageChannel(androidx.browser.customtabs.CustomTabsSessionToken!, android.net.Uri!);
+    method protected abstract boolean updateVisuals(androidx.browser.customtabs.CustomTabsSessionToken!, android.os.Bundle!);
+    method protected abstract boolean validateRelationship(androidx.browser.customtabs.CustomTabsSessionToken!, @androidx.browser.customtabs.CustomTabsService.Relation int, android.net.Uri!, android.os.Bundle!);
+    method protected abstract boolean warmup(long);
+    field public static final String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";
+    field public static final String CATEGORY_COLOR_SCHEME_CUSTOMIZATION = "androidx.browser.customtabs.category.ColorSchemeCustomization";
+    field public static final String CATEGORY_NAVBAR_COLOR_CUSTOMIZATION = "androidx.browser.customtabs.category.NavBarColorCustomization";
+    field public static final int FILE_PURPOSE_TRUSTED_WEB_ACTIVITY_SPLASH_IMAGE = 1; // 0x1
+    field public static final String KEY_URL = "android.support.customtabs.otherurls.URL";
+    field public static final int RELATION_HANDLE_ALL_URLS = 2; // 0x2
+    field public static final int RELATION_USE_AS_ORIGIN = 1; // 0x1
+    field public static final int RESULT_FAILURE_DISALLOWED = -1; // 0xffffffff
+    field public static final int RESULT_FAILURE_MESSAGING_ERROR = -3; // 0xfffffffd
+    field public static final int RESULT_FAILURE_REMOTE_ERROR = -2; // 0xfffffffe
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+    field public static final String TRUSTED_WEB_ACTIVITY_CATEGORY = "androidx.browser.trusted.category.TrustedWebActivities";
+  }
+
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({androidx.browser.customtabs.CustomTabsService.RELATION_USE_AS_ORIGIN, androidx.browser.customtabs.CustomTabsService.RELATION_HANDLE_ALL_URLS}) public static @interface CustomTabsService.Relation {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({androidx.browser.customtabs.CustomTabsService.RESULT_SUCCESS, androidx.browser.customtabs.CustomTabsService.RESULT_FAILURE_DISALLOWED, androidx.browser.customtabs.CustomTabsService.RESULT_FAILURE_REMOTE_ERROR, androidx.browser.customtabs.CustomTabsService.RESULT_FAILURE_MESSAGING_ERROR}) public static @interface CustomTabsService.Result {
+  }
+
+  public abstract class CustomTabsServiceConnection implements android.content.ServiceConnection {
+    ctor public CustomTabsServiceConnection();
+    method public abstract void onCustomTabsServiceConnected(android.content.ComponentName!, androidx.browser.customtabs.CustomTabsClient!);
+    method public final void onServiceConnected(android.content.ComponentName!, android.os.IBinder!);
+  }
+
+  public final class CustomTabsSession {
+    method @VisibleForTesting public static androidx.browser.customtabs.CustomTabsSession createMockSessionForTesting(android.content.ComponentName);
+    method public boolean mayLaunchUrl(android.net.Uri!, android.os.Bundle!, java.util.List<android.os.Bundle!>!);
+    method @androidx.browser.customtabs.CustomTabsService.Result public int postMessage(String!, android.os.Bundle!);
+    method public boolean receiveFile(android.net.Uri, @androidx.browser.customtabs.CustomTabsService.FilePurpose int, android.os.Bundle?);
+    method public boolean requestPostMessageChannel(android.net.Uri!);
+    method public boolean setActionButton(android.graphics.Bitmap, String);
+    method public boolean setSecondaryToolbarViews(android.widget.RemoteViews?, int[]?, android.app.PendingIntent?);
+    method @Deprecated public boolean setToolbarItem(int, android.graphics.Bitmap, String);
+    method public boolean validateRelationship(@androidx.browser.customtabs.CustomTabsService.Relation int, android.net.Uri, android.os.Bundle?);
+  }
+
+
+  public class CustomTabsSessionToken {
+    method public static androidx.browser.customtabs.CustomTabsSessionToken createMockSessionTokenForTesting();
+    method public androidx.browser.customtabs.CustomTabsCallback? getCallback();
+    method public static androidx.browser.customtabs.CustomTabsSessionToken? getSessionTokenFromIntent(android.content.Intent);
+    method public boolean isAssociatedWith(androidx.browser.customtabs.CustomTabsSession);
+  }
+
+
+  public class PostMessageService extends android.app.Service {
+    ctor public PostMessageService();
+    method public android.os.IBinder! onBind(android.content.Intent!);
+  }
+
+  public abstract class PostMessageServiceConnection implements androidx.browser.customtabs.PostMessageBackend android.content.ServiceConnection {
+    ctor public PostMessageServiceConnection(androidx.browser.customtabs.CustomTabsSessionToken!);
+    method public boolean bindSessionToPostMessageService(android.content.Context!, String!);
+    method public final boolean notifyMessageChannelReady(android.os.Bundle!);
+    method public void onPostMessageServiceConnected();
+    method public void onPostMessageServiceDisconnected();
+    method public final void onServiceConnected(android.content.ComponentName!, android.os.IBinder!);
+    method public final void onServiceDisconnected(android.content.ComponentName!);
+    method public final boolean postMessage(String!, android.os.Bundle!);
+    method public void unbindFromContext(android.content.Context!);
+  }
+
+  public class TrustedWebUtils {
+    method @Deprecated public static void launchAsTrustedWebActivity(android.content.Context, androidx.browser.customtabs.CustomTabsIntent, android.net.Uri);
+    method public static boolean splashScreensAreSupported(android.content.Context, String, String);
+    method @WorkerThread public static boolean transferSplashImage(android.content.Context, java.io.File, String, String, androidx.browser.customtabs.CustomTabsSession);
+    field public static final String EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY = "android.support.customtabs.extra.LAUNCH_AS_TRUSTED_WEB_ACTIVITY";
+  }
+
+}
+
+package androidx.browser.trusted {
+
+
+  public class TrustedWebActivityIntentBuilder {
+    ctor public TrustedWebActivityIntentBuilder(android.net.Uri);
+    method public android.content.Intent build(androidx.browser.customtabs.CustomTabsSession);
+    method public androidx.browser.customtabs.CustomTabsIntent buildCustomTabsIntent();
+    method public android.net.Uri getUrl();
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setAdditionalTrustedOrigins(java.util.List<java.lang.String!>);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setColorScheme(int);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setColorSchemeParams(int, androidx.browser.customtabs.CustomTabColorSchemeParams);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setSplashScreenParams(android.os.Bundle);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setToolbarColor(@ColorInt int);
+    field public static final String EXTRA_ADDITIONAL_TRUSTED_ORIGINS = "android.support.customtabs.extra.ADDITIONAL_TRUSTED_ORIGINS";
+    field public static final String EXTRA_SPLASH_SCREEN_PARAMS = "androidx.browser.trusted.EXTRA_SPLASH_SCREEN_PARAMS";
+  }
+
+
+
+
+
+}
+
+package androidx.browser.trusted.splashscreens {
+
+  public final class SplashScreenParamKey {
+    field public static final String BACKGROUND_COLOR = "androidx.browser.trusted.trusted.KEY_SPLASH_SCREEN_BACKGROUND_COLOR";
+    field public static final String FADE_OUT_DURATION_MS = "androidx.browser.trusted.KEY_SPLASH_SCREEN_FADE_OUT_DURATION";
+    field public static final String IMAGE_TRANSFORMATION_MATRIX = "androidx.browser.trusted.KEY_SPLASH_SCREEN_TRANSFORMATION_MATRIX";
+    field public static final String SCALE_TYPE = "androidx.browser.trusted.KEY_SPLASH_SCREEN_SCALE_TYPE";
+    field public static final String VERSION = "androidx.browser.trusted.KEY_SPLASH_SCREEN_VERSION";
+  }
+
+  public final class SplashScreenVersion {
+    field public static final String V1 = "androidx.browser.trusted.category.TrustedWebActivitySplashScreensV1";
+  }
+
+}
+
diff --git a/browser/api/restricted_1.2.0-alpha07.txt b/browser/api/restricted_1.2.0-alpha07.txt
new file mode 100644
index 0000000..9cdf40e
--- /dev/null
+++ b/browser/api/restricted_1.2.0-alpha07.txt
@@ -0,0 +1,333 @@
+// Signature format: 3.0
+package androidx.browser.browseractions {
+
+  @Deprecated public class BrowserActionItem {
+    ctor @Deprecated public BrowserActionItem(String, android.app.PendingIntent, @DrawableRes int);
+    ctor @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public BrowserActionItem(String, android.app.PendingIntent, android.net.Uri);
+    ctor @Deprecated public BrowserActionItem(String, android.app.PendingIntent);
+    method @Deprecated public android.app.PendingIntent getAction();
+    method @Deprecated public int getIconId();
+    method @Deprecated public String getTitle();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class BrowserActionsFallbackMenuView extends android.widget.LinearLayout {
+    ctor public BrowserActionsFallbackMenuView(android.content.Context!, android.util.AttributeSet!);
+  }
+
+  @Deprecated public class BrowserActionsIntent {
+    method @Deprecated public static String? getCreatorPackageName(android.content.Intent);
+    method @Deprecated public android.content.Intent getIntent();
+    method @Deprecated public static String? getUntrustedCreatorPackageName(android.content.Intent);
+    method @Deprecated public static void launchIntent(android.content.Context!, android.content.Intent!);
+    method @Deprecated public static void openBrowserAction(android.content.Context!, android.net.Uri!);
+    method @Deprecated public static void openBrowserAction(android.content.Context!, android.net.Uri!, int, java.util.ArrayList<androidx.browser.browseractions.BrowserActionItem!>!, android.app.PendingIntent!);
+    method @Deprecated public static java.util.List<androidx.browser.browseractions.BrowserActionItem!>! parseBrowserActionItems(java.util.ArrayList<android.os.Bundle!>!);
+    field @Deprecated public static final String ACTION_BROWSER_ACTIONS_OPEN = "androidx.browser.browseractions.browser_action_open";
+    field @Deprecated public static final String EXTRA_APP_ID = "androidx.browser.browseractions.APP_ID";
+    field @Deprecated public static final String EXTRA_MENU_ITEMS = "androidx.browser.browseractions.extra.MENU_ITEMS";
+    field @Deprecated public static final String EXTRA_SELECTED_ACTION_PENDING_INTENT = "androidx.browser.browseractions.extra.SELECTED_ACTION_PENDING_INTENT";
+    field @Deprecated public static final String EXTRA_TYPE = "androidx.browser.browseractions.extra.TYPE";
+    field @Deprecated public static final int ITEM_COPY = 3; // 0x3
+    field @Deprecated public static final int ITEM_DOWNLOAD = 2; // 0x2
+    field @Deprecated public static final int ITEM_INVALID_ITEM = -1; // 0xffffffff
+    field @Deprecated public static final int ITEM_OPEN_IN_INCOGNITO = 1; // 0x1
+    field @Deprecated public static final int ITEM_OPEN_IN_NEW_TAB = 0; // 0x0
+    field @Deprecated public static final int ITEM_SHARE = 4; // 0x4
+    field @Deprecated public static final String KEY_ACTION = "androidx.browser.browseractions.ACTION";
+    field @Deprecated public static final String KEY_ICON_ID = "androidx.browser.browseractions.ICON_ID";
+    field @Deprecated public static final String KEY_TITLE = "androidx.browser.browseractions.TITLE";
+    field @Deprecated public static final int MAX_CUSTOM_ITEMS = 5; // 0x5
+    field @Deprecated public static final int URL_TYPE_AUDIO = 3; // 0x3
+    field @Deprecated public static final int URL_TYPE_FILE = 4; // 0x4
+    field @Deprecated public static final int URL_TYPE_IMAGE = 1; // 0x1
+    field @Deprecated public static final int URL_TYPE_NONE = 0; // 0x0
+    field @Deprecated public static final int URL_TYPE_PLUGIN = 5; // 0x5
+    field @Deprecated public static final int URL_TYPE_VIDEO = 2; // 0x2
+  }
+
+  @Deprecated @IntDef({androidx.browser.browseractions.BrowserActionsIntent.ITEM_INVALID_ITEM, androidx.browser.browseractions.BrowserActionsIntent.ITEM_OPEN_IN_NEW_TAB, androidx.browser.browseractions.BrowserActionsIntent.ITEM_OPEN_IN_INCOGNITO, androidx.browser.browseractions.BrowserActionsIntent.ITEM_DOWNLOAD, androidx.browser.browseractions.BrowserActionsIntent.ITEM_COPY, androidx.browser.browseractions.BrowserActionsIntent.ITEM_SHARE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface BrowserActionsIntent.BrowserActionsItemId {
+  }
+
+  @Deprecated @IntDef({androidx.browser.browseractions.BrowserActionsIntent.URL_TYPE_NONE, androidx.browser.browseractions.BrowserActionsIntent.URL_TYPE_IMAGE, androidx.browser.browseractions.BrowserActionsIntent.URL_TYPE_VIDEO, androidx.browser.browseractions.BrowserActionsIntent.URL_TYPE_AUDIO, androidx.browser.browseractions.BrowserActionsIntent.URL_TYPE_FILE, androidx.browser.browseractions.BrowserActionsIntent.URL_TYPE_PLUGIN}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface BrowserActionsIntent.BrowserActionsUrlType {
+  }
+
+  @Deprecated public static final class BrowserActionsIntent.Builder {
+    ctor @Deprecated public BrowserActionsIntent.Builder(android.content.Context!, android.net.Uri!);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent! build();
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setCustomItems(java.util.ArrayList<androidx.browser.browseractions.BrowserActionItem!>!);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setCustomItems(androidx.browser.browseractions.BrowserActionItem!...);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setOnItemSelectedAction(android.app.PendingIntent!);
+    method @Deprecated public androidx.browser.browseractions.BrowserActionsIntent.Builder! setUrlType(@androidx.browser.browseractions.BrowserActionsIntent.BrowserActionsUrlType int);
+  }
+
+
+}
+
+package androidx.browser.customtabs {
+
+  public final class CustomTabColorSchemeParams {
+    field @ColorInt public final Integer? navigationBarColor;
+    field @ColorInt public final Integer? secondaryToolbarColor;
+    field @ColorInt public final Integer? toolbarColor;
+  }
+
+  public static final class CustomTabColorSchemeParams.Builder {
+    ctor public CustomTabColorSchemeParams.Builder();
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams build();
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams.Builder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams.Builder setSecondaryToolbarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabColorSchemeParams.Builder setToolbarColor(@ColorInt int);
+  }
+
+  public class CustomTabsCallback {
+    ctor public CustomTabsCallback();
+    method public void extraCallback(String!, android.os.Bundle!);
+    method public void onMessageChannelReady(android.os.Bundle!);
+    method public void onNavigationEvent(int, android.os.Bundle!);
+    method public void onPostMessage(String!, android.os.Bundle!);
+    method public void onRelationshipValidationResult(@androidx.browser.customtabs.CustomTabsService.Relation int, android.net.Uri!, boolean, android.os.Bundle!);
+    field public static final int NAVIGATION_ABORTED = 4; // 0x4
+    field public static final int NAVIGATION_FAILED = 3; // 0x3
+    field public static final int NAVIGATION_FINISHED = 2; // 0x2
+    field public static final int NAVIGATION_STARTED = 1; // 0x1
+    field public static final int TAB_HIDDEN = 6; // 0x6
+    field public static final int TAB_SHOWN = 5; // 0x5
+  }
+
+  public class CustomTabsClient {
+    method public static boolean bindCustomTabsService(android.content.Context, String?, androidx.browser.customtabs.CustomTabsServiceConnection);
+    method public static boolean connectAndInitialize(android.content.Context, String);
+    method public android.os.Bundle? extraCommand(String, android.os.Bundle?);
+    method public static String? getPackageName(android.content.Context, java.util.List<java.lang.String!>?);
+    method public static String? getPackageName(android.content.Context, java.util.List<java.lang.String!>?, boolean);
+    method public androidx.browser.customtabs.CustomTabsSession? newSession(androidx.browser.customtabs.CustomTabsCallback?);
+    method public androidx.browser.customtabs.CustomTabsSession? newSession(androidx.browser.customtabs.CustomTabsCallback?, int);
+    method public boolean warmup(long);
+  }
+
+  public final class CustomTabsIntent {
+    method public static androidx.browser.customtabs.CustomTabColorSchemeParams getColorSchemeParams(android.content.Intent, @androidx.browser.customtabs.CustomTabsIntent.ColorScheme int);
+    method public static int getMaxToolbarItems();
+    method public void launchUrl(android.content.Context!, android.net.Uri!);
+    method public static android.content.Intent! setAlwaysUseBrowserUI(android.content.Intent!);
+    method public static boolean shouldAlwaysUseBrowserUI(android.content.Intent!);
+    field public static final int COLOR_SCHEME_DARK = 2; // 0x2
+    field public static final int COLOR_SCHEME_LIGHT = 1; // 0x1
+    field public static final int COLOR_SCHEME_SYSTEM = 0; // 0x0
+    field public static final String EXTRA_ACTION_BUTTON_BUNDLE = "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
+    field public static final String EXTRA_CLOSE_BUTTON_ICON = "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
+    field public static final String EXTRA_COLOR_SCHEME = "androidx.browser.customtabs.extra.COLOR_SCHEME";
+    field public static final String EXTRA_COLOR_SCHEME_PARAMS = "androidx.browser.customtabs.extra.COLOR_SCHEME_PARAMS";
+    field public static final String EXTRA_DEFAULT_SHARE_MENU_ITEM = "android.support.customtabs.extra.SHARE_MENU_ITEM";
+    field public static final String EXTRA_ENABLE_INSTANT_APPS = "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
+    field public static final String EXTRA_ENABLE_URLBAR_HIDING = "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
+    field public static final String EXTRA_EXIT_ANIMATION_BUNDLE = "android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
+    field public static final String EXTRA_MENU_ITEMS = "android.support.customtabs.extra.MENU_ITEMS";
+    field public static final String EXTRA_NAVIGATION_BAR_COLOR = "androidx.browser.customtabs.extra.NAVIGATION_BAR_COLOR";
+    field public static final String EXTRA_REMOTEVIEWS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS";
+    field public static final String EXTRA_REMOTEVIEWS_CLICKED_ID = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID";
+    field public static final String EXTRA_REMOTEVIEWS_PENDINGINTENT = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_PENDINGINTENT";
+    field public static final String EXTRA_REMOTEVIEWS_VIEW_IDS = "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_VIEW_IDS";
+    field public static final String EXTRA_SECONDARY_TOOLBAR_COLOR = "android.support.customtabs.extra.SECONDARY_TOOLBAR_COLOR";
+    field public static final String EXTRA_SESSION = "android.support.customtabs.extra.SESSION";
+    field public static final String EXTRA_TINT_ACTION_BUTTON = "android.support.customtabs.extra.TINT_ACTION_BUTTON";
+    field public static final String EXTRA_TITLE_VISIBILITY_STATE = "android.support.customtabs.extra.TITLE_VISIBILITY";
+    field public static final String EXTRA_TOOLBAR_COLOR = "android.support.customtabs.extra.TOOLBAR_COLOR";
+    field public static final String EXTRA_TOOLBAR_ITEMS = "android.support.customtabs.extra.TOOLBAR_ITEMS";
+    field public static final String KEY_DESCRIPTION = "android.support.customtabs.customaction.DESCRIPTION";
+    field public static final String KEY_ICON = "android.support.customtabs.customaction.ICON";
+    field public static final String KEY_ID = "android.support.customtabs.customaction.ID";
+    field public static final String KEY_MENU_ITEM_TITLE = "android.support.customtabs.customaction.MENU_ITEM_TITLE";
+    field public static final String KEY_PENDING_INTENT = "android.support.customtabs.customaction.PENDING_INTENT";
+    field public static final int NO_TITLE = 0; // 0x0
+    field public static final int SHOW_PAGE_TITLE = 1; // 0x1
+    field public static final int TOOLBAR_ACTION_BUTTON_ID = 0; // 0x0
+    field public final android.content.Intent intent;
+    field public final android.os.Bundle? startAnimationBundle;
+  }
+
+  public static final class CustomTabsIntent.Builder {
+    ctor public CustomTabsIntent.Builder();
+    ctor public CustomTabsIntent.Builder(androidx.browser.customtabs.CustomTabsSession?);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder addDefaultShareMenuItem();
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder addMenuItem(String, android.app.PendingIntent);
+    method @Deprecated public androidx.browser.customtabs.CustomTabsIntent.Builder addToolbarItem(int, android.graphics.Bitmap, String, android.app.PendingIntent!) throws java.lang.IllegalStateException;
+    method public androidx.browser.customtabs.CustomTabsIntent build();
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder enableUrlBarHiding();
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, String, android.app.PendingIntent, boolean);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setActionButton(android.graphics.Bitmap, String, android.app.PendingIntent);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setCloseButtonIcon(android.graphics.Bitmap);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setColorScheme(@androidx.browser.customtabs.CustomTabsIntent.ColorScheme int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setColorSchemeParams(@androidx.browser.customtabs.CustomTabsIntent.ColorScheme int, androidx.browser.customtabs.CustomTabColorSchemeParams);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setExitAnimations(android.content.Context, @AnimRes int, @AnimRes int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setInstantAppsEnabled(boolean);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSecondaryToolbarColor(@ColorInt int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSecondaryToolbarViews(android.widget.RemoteViews, int[]?, android.app.PendingIntent?);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSession(androidx.browser.customtabs.CustomTabsSession);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setShowTitle(boolean);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setStartAnimations(android.content.Context, @AnimRes int, @AnimRes int);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setToolbarColor(@ColorInt int);
+  }
+
+
+  public abstract class CustomTabsService extends android.app.Service {
+    ctor public CustomTabsService();
+    method protected boolean cleanUpSession(androidx.browser.customtabs.CustomTabsSessionToken!);
+    method protected abstract android.os.Bundle! extraCommand(String!, android.os.Bundle!);
+    method protected abstract boolean mayLaunchUrl(androidx.browser.customtabs.CustomTabsSessionToken!, android.net.Uri!, android.os.Bundle!, java.util.List<android.os.Bundle!>!);
+    method protected abstract boolean newSession(androidx.browser.customtabs.CustomTabsSessionToken!);
+    method public android.os.IBinder! onBind(android.content.Intent!);
+    method @androidx.browser.customtabs.CustomTabsService.Result protected abstract int postMessage(androidx.browser.customtabs.CustomTabsSessionToken!, String!, android.os.Bundle!);
+    method protected abstract boolean receiveFile(androidx.browser.customtabs.CustomTabsSessionToken, android.net.Uri, @androidx.browser.customtabs.CustomTabsService.FilePurpose int, android.os.Bundle?);
+    method protected abstract boolean requestPostMessageChannel(androidx.browser.customtabs.CustomTabsSessionToken!, android.net.Uri!);
+    method protected abstract boolean updateVisuals(androidx.browser.customtabs.CustomTabsSessionToken!, android.os.Bundle!);
+    method protected abstract boolean validateRelationship(androidx.browser.customtabs.CustomTabsSessionToken!, @androidx.browser.customtabs.CustomTabsService.Relation int, android.net.Uri!, android.os.Bundle!);
+    method protected abstract boolean warmup(long);
+    field public static final String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";
+    field public static final String CATEGORY_COLOR_SCHEME_CUSTOMIZATION = "androidx.browser.customtabs.category.ColorSchemeCustomization";
+    field public static final String CATEGORY_NAVBAR_COLOR_CUSTOMIZATION = "androidx.browser.customtabs.category.NavBarColorCustomization";
+    field public static final int FILE_PURPOSE_TRUSTED_WEB_ACTIVITY_SPLASH_IMAGE = 1; // 0x1
+    field public static final String KEY_URL = "android.support.customtabs.otherurls.URL";
+    field public static final int RELATION_HANDLE_ALL_URLS = 2; // 0x2
+    field public static final int RELATION_USE_AS_ORIGIN = 1; // 0x1
+    field public static final int RESULT_FAILURE_DISALLOWED = -1; // 0xffffffff
+    field public static final int RESULT_FAILURE_MESSAGING_ERROR = -3; // 0xfffffffd
+    field public static final int RESULT_FAILURE_REMOTE_ERROR = -2; // 0xfffffffe
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+    field public static final String TRUSTED_WEB_ACTIVITY_CATEGORY = "androidx.browser.trusted.category.TrustedWebActivities";
+  }
+
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({androidx.browser.customtabs.CustomTabsService.RELATION_USE_AS_ORIGIN, androidx.browser.customtabs.CustomTabsService.RELATION_HANDLE_ALL_URLS}) public static @interface CustomTabsService.Relation {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({androidx.browser.customtabs.CustomTabsService.RESULT_SUCCESS, androidx.browser.customtabs.CustomTabsService.RESULT_FAILURE_DISALLOWED, androidx.browser.customtabs.CustomTabsService.RESULT_FAILURE_REMOTE_ERROR, androidx.browser.customtabs.CustomTabsService.RESULT_FAILURE_MESSAGING_ERROR}) public static @interface CustomTabsService.Result {
+  }
+
+  public abstract class CustomTabsServiceConnection implements android.content.ServiceConnection {
+    ctor public CustomTabsServiceConnection();
+    method public abstract void onCustomTabsServiceConnected(android.content.ComponentName!, androidx.browser.customtabs.CustomTabsClient!);
+    method public final void onServiceConnected(android.content.ComponentName!, android.os.IBinder!);
+  }
+
+  public final class CustomTabsSession {
+    method @VisibleForTesting public static androidx.browser.customtabs.CustomTabsSession createMockSessionForTesting(android.content.ComponentName);
+    method public boolean mayLaunchUrl(android.net.Uri!, android.os.Bundle!, java.util.List<android.os.Bundle!>!);
+    method @androidx.browser.customtabs.CustomTabsService.Result public int postMessage(String!, android.os.Bundle!);
+    method public boolean receiveFile(android.net.Uri, @androidx.browser.customtabs.CustomTabsService.FilePurpose int, android.os.Bundle?);
+    method public boolean requestPostMessageChannel(android.net.Uri!);
+    method public boolean setActionButton(android.graphics.Bitmap, String);
+    method public boolean setSecondaryToolbarViews(android.widget.RemoteViews?, int[]?, android.app.PendingIntent?);
+    method @Deprecated public boolean setToolbarItem(int, android.graphics.Bitmap, String);
+    method public boolean validateRelationship(@androidx.browser.customtabs.CustomTabsService.Relation int, android.net.Uri, android.os.Bundle?);
+  }
+
+
+  public class CustomTabsSessionToken {
+    method public static androidx.browser.customtabs.CustomTabsSessionToken createMockSessionTokenForTesting();
+    method public androidx.browser.customtabs.CustomTabsCallback? getCallback();
+    method public static androidx.browser.customtabs.CustomTabsSessionToken? getSessionTokenFromIntent(android.content.Intent);
+    method public boolean isAssociatedWith(androidx.browser.customtabs.CustomTabsSession);
+  }
+
+
+  public class PostMessageService extends android.app.Service {
+    ctor public PostMessageService();
+    method public android.os.IBinder! onBind(android.content.Intent!);
+  }
+
+  public abstract class PostMessageServiceConnection implements androidx.browser.customtabs.PostMessageBackend android.content.ServiceConnection {
+    ctor public PostMessageServiceConnection(androidx.browser.customtabs.CustomTabsSessionToken!);
+    method public boolean bindSessionToPostMessageService(android.content.Context!, String!);
+    method public final boolean notifyMessageChannelReady(android.os.Bundle!);
+    method public void onPostMessageServiceConnected();
+    method public void onPostMessageServiceDisconnected();
+    method public final void onServiceConnected(android.content.ComponentName!, android.os.IBinder!);
+    method public final void onServiceDisconnected(android.content.ComponentName!);
+    method public final boolean postMessage(String!, android.os.Bundle!);
+    method public void unbindFromContext(android.content.Context!);
+  }
+
+  public class TrustedWebUtils {
+    method @Deprecated public static void launchAsTrustedWebActivity(android.content.Context, androidx.browser.customtabs.CustomTabsIntent, android.net.Uri);
+    method public static boolean splashScreensAreSupported(android.content.Context, String, String);
+    method @WorkerThread public static boolean transferSplashImage(android.content.Context, java.io.File, String, String, androidx.browser.customtabs.CustomTabsSession);
+    field public static final String EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY = "android.support.customtabs.extra.LAUNCH_AS_TRUSTED_WEB_ACTIVITY";
+  }
+
+}
+
+package androidx.browser.trusted {
+
+
+  public class TrustedWebActivityIntentBuilder {
+    ctor public TrustedWebActivityIntentBuilder(android.net.Uri);
+    method public android.content.Intent build(androidx.browser.customtabs.CustomTabsSession);
+    method public androidx.browser.customtabs.CustomTabsIntent buildCustomTabsIntent();
+    method public android.net.Uri getUrl();
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setAdditionalTrustedOrigins(java.util.List<java.lang.String!>);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setColorScheme(int);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setColorSchemeParams(int, androidx.browser.customtabs.CustomTabColorSchemeParams);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setSplashScreenParams(android.os.Bundle);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setToolbarColor(@ColorInt int);
+    field public static final String EXTRA_ADDITIONAL_TRUSTED_ORIGINS = "android.support.customtabs.extra.ADDITIONAL_TRUSTED_ORIGINS";
+    field public static final String EXTRA_SPLASH_SCREEN_PARAMS = "androidx.browser.trusted.EXTRA_SPLASH_SCREEN_PARAMS";
+  }
+
+  public class TrustedWebActivityService extends android.app.Service {
+    ctor public TrustedWebActivityService();
+    method public boolean areNotificationsEnabled(String);
+    method public void cancelNotification(String, int);
+    method public android.os.Bundle getSmallIconBitmap();
+    method public int getSmallIconId();
+    method public boolean notifyNotificationWithChannel(String, int, android.app.Notification, String);
+    method public final android.os.IBinder? onBind(android.content.Intent?);
+    method public final boolean onUnbind(android.content.Intent?);
+    method public static final void setVerifiedProvider(android.content.Context, String?);
+    field public static final String ACTION_TRUSTED_WEB_ACTIVITY_SERVICE = "android.support.customtabs.trusted.TRUSTED_WEB_ACTIVITY_SERVICE";
+    field public static final String KEY_SMALL_ICON_BITMAP = "android.support.customtabs.trusted.SMALL_ICON_BITMAP";
+    field public static final String META_DATA_NAME_SMALL_ICON = "android.support.customtabs.trusted.SMALL_ICON";
+    field public static final int SMALL_ICON_NOT_SET = -1; // 0xffffffff
+  }
+
+  public class TrustedWebActivityServiceConnectionManager {
+    ctor public TrustedWebActivityServiceConnectionManager(android.content.Context);
+    method @MainThread public boolean execute(android.net.Uri, String, androidx.browser.trusted.TrustedWebActivityServiceConnectionManager.ExecutionCallback);
+    method public static java.util.Set<java.lang.String!> getVerifiedPackages(android.content.Context, String);
+    method public static void registerClient(android.content.Context, String, String);
+    method @MainThread public boolean serviceExistsForScope(android.net.Uri, String);
+  }
+
+  public static interface TrustedWebActivityServiceConnectionManager.ExecutionCallback {
+    method public void onConnected(androidx.browser.trusted.TrustedWebActivityServiceWrapper?) throws android.os.RemoteException;
+  }
+
+  public class TrustedWebActivityServiceWrapper {
+    method public boolean areNotificationsEnabled(String);
+    method public void cancel(String, int);
+    method public android.content.ComponentName getComponentName();
+    method public android.graphics.Bitmap? getSmallIconBitmap();
+    method public int getSmallIconId();
+    method public boolean notify(String, int, android.app.Notification, String);
+  }
+
+}
+
+package androidx.browser.trusted.splashscreens {
+
+  public final class SplashScreenParamKey {
+    field public static final String BACKGROUND_COLOR = "androidx.browser.trusted.trusted.KEY_SPLASH_SCREEN_BACKGROUND_COLOR";
+    field public static final String FADE_OUT_DURATION_MS = "androidx.browser.trusted.KEY_SPLASH_SCREEN_FADE_OUT_DURATION";
+    field public static final String IMAGE_TRANSFORMATION_MATRIX = "androidx.browser.trusted.KEY_SPLASH_SCREEN_TRANSFORMATION_MATRIX";
+    field public static final String SCALE_TYPE = "androidx.browser.trusted.KEY_SPLASH_SCREEN_SCALE_TYPE";
+    field public static final String VERSION = "androidx.browser.trusted.KEY_SPLASH_SCREEN_VERSION";
+  }
+
+  public final class SplashScreenVersion {
+    field public static final String V1 = "androidx.browser.trusted.category.TrustedWebActivitySplashScreensV1";
+  }
+
+}
+
diff --git a/browser/api/restricted_current.txt b/browser/api/restricted_current.txt
index 8a1533f..9cdf40e 100644
--- a/browser/api/restricted_current.txt
+++ b/browser/api/restricted_current.txt
@@ -164,6 +164,7 @@
     method public androidx.browser.customtabs.CustomTabsIntent.Builder setNavigationBarColor(@ColorInt int);
     method public androidx.browser.customtabs.CustomTabsIntent.Builder setSecondaryToolbarColor(@ColorInt int);
     method public androidx.browser.customtabs.CustomTabsIntent.Builder setSecondaryToolbarViews(android.widget.RemoteViews, int[]?, android.app.PendingIntent?);
+    method public androidx.browser.customtabs.CustomTabsIntent.Builder setSession(androidx.browser.customtabs.CustomTabsSession);
     method public androidx.browser.customtabs.CustomTabsIntent.Builder setShowTitle(boolean);
     method public androidx.browser.customtabs.CustomTabsIntent.Builder setStartAnimations(android.content.Context, @AnimRes int, @AnimRes int);
     method public androidx.browser.customtabs.CustomTabsIntent.Builder setToolbarColor(@ColorInt int);
@@ -260,19 +261,57 @@
 package androidx.browser.trusted {
 
 
-  public class TrustedWebActivityBuilder {
-    ctor public TrustedWebActivityBuilder(android.content.Context, android.net.Uri);
+  public class TrustedWebActivityIntentBuilder {
+    ctor public TrustedWebActivityIntentBuilder(android.net.Uri);
+    method public android.content.Intent build(androidx.browser.customtabs.CustomTabsSession);
+    method public androidx.browser.customtabs.CustomTabsIntent buildCustomTabsIntent();
     method public android.net.Uri getUrl();
-    method public void launchActivity(androidx.browser.customtabs.CustomTabsSession);
-    method public androidx.browser.trusted.TrustedWebActivityBuilder setAdditionalTrustedOrigins(java.util.List<java.lang.String!>);
-    method public androidx.browser.trusted.TrustedWebActivityBuilder setSplashScreenParams(android.os.Bundle);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setAdditionalTrustedOrigins(java.util.List<java.lang.String!>);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setColorScheme(int);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setColorSchemeParams(int, androidx.browser.customtabs.CustomTabColorSchemeParams);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setNavigationBarColor(@ColorInt int);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setSplashScreenParams(android.os.Bundle);
+    method public androidx.browser.trusted.TrustedWebActivityIntentBuilder setToolbarColor(@ColorInt int);
     field public static final String EXTRA_ADDITIONAL_TRUSTED_ORIGINS = "android.support.customtabs.extra.ADDITIONAL_TRUSTED_ORIGINS";
     field public static final String EXTRA_SPLASH_SCREEN_PARAMS = "androidx.browser.trusted.EXTRA_SPLASH_SCREEN_PARAMS";
   }
 
+  public class TrustedWebActivityService extends android.app.Service {
+    ctor public TrustedWebActivityService();
+    method public boolean areNotificationsEnabled(String);
+    method public void cancelNotification(String, int);
+    method public android.os.Bundle getSmallIconBitmap();
+    method public int getSmallIconId();
+    method public boolean notifyNotificationWithChannel(String, int, android.app.Notification, String);
+    method public final android.os.IBinder? onBind(android.content.Intent?);
+    method public final boolean onUnbind(android.content.Intent?);
+    method public static final void setVerifiedProvider(android.content.Context, String?);
+    field public static final String ACTION_TRUSTED_WEB_ACTIVITY_SERVICE = "android.support.customtabs.trusted.TRUSTED_WEB_ACTIVITY_SERVICE";
+    field public static final String KEY_SMALL_ICON_BITMAP = "android.support.customtabs.trusted.SMALL_ICON_BITMAP";
+    field public static final String META_DATA_NAME_SMALL_ICON = "android.support.customtabs.trusted.SMALL_ICON";
+    field public static final int SMALL_ICON_NOT_SET = -1; // 0xffffffff
+  }
 
+  public class TrustedWebActivityServiceConnectionManager {
+    ctor public TrustedWebActivityServiceConnectionManager(android.content.Context);
+    method @MainThread public boolean execute(android.net.Uri, String, androidx.browser.trusted.TrustedWebActivityServiceConnectionManager.ExecutionCallback);
+    method public static java.util.Set<java.lang.String!> getVerifiedPackages(android.content.Context, String);
+    method public static void registerClient(android.content.Context, String, String);
+    method @MainThread public boolean serviceExistsForScope(android.net.Uri, String);
+  }
 
+  public static interface TrustedWebActivityServiceConnectionManager.ExecutionCallback {
+    method public void onConnected(androidx.browser.trusted.TrustedWebActivityServiceWrapper?) throws android.os.RemoteException;
+  }
 
+  public class TrustedWebActivityServiceWrapper {
+    method public boolean areNotificationsEnabled(String);
+    method public void cancel(String, int);
+    method public android.content.ComponentName getComponentName();
+    method public android.graphics.Bitmap? getSmallIconBitmap();
+    method public int getSmallIconId();
+    method public boolean notify(String, int, android.app.Notification, String);
+  }
 
 }
 
diff --git a/browser/build.gradle b/browser/build.gradle
index 88a21283..5dd3789 100644
--- a/browser/build.gradle
+++ b/browser/build.gradle
@@ -12,15 +12,17 @@
     defaultConfig {
         minSdkVersion 16
     }
+
+    testOptions.unitTests.includeAndroidResources = true
 }
 
 dependencies {
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     api("androidx.annotation:annotation:1.1.0")
-    api(project(":interpolator"))
-    implementation("androidx.collection:collection:1.1.0")
 
-    implementation(project(":concurrent:concurrent-futures"))
+    implementation("androidx.collection:collection:1.1.0")
+    implementation("androidx.concurrent:concurrent-futures:1.0.0-beta01")
+    implementation("androidx.interpolator:interpolator:1.0.0")
 
     annotationProcessor(NULLAWAY)
 
diff --git a/browser/src/androidTest/java/androidx/browser/trusted/TestTrustedWebActivityService.java b/browser/src/androidTest/java/androidx/browser/trusted/TestTrustedWebActivityService.java
index d4f87fd..37574f0 100644
--- a/browser/src/androidTest/java/androidx/browser/trusted/TestTrustedWebActivityService.java
+++ b/browser/src/androidTest/java/androidx/browser/trusted/TestTrustedWebActivityService.java
@@ -19,26 +19,28 @@
 import android.app.Notification;
 import android.os.Parcelable;
 
+import androidx.annotation.NonNull;
+
 public class TestTrustedWebActivityService extends TrustedWebActivityService {
     public static final int SMALL_ICON_ID = 666;
 
     @Override
-    protected boolean notifyNotificationWithChannel(String platformTag, int platformId,
-            Notification notification, String channelName) {
+    public boolean notifyNotificationWithChannel(@NonNull String platformTag, int platformId,
+            @NonNull Notification notification, @NonNull String channelName) {
         return true;
     }
 
     @Override
-    protected void cancelNotification(String platformTag, int platformId) {
+    public void cancelNotification(@NonNull String platformTag, int platformId) {
     }
 
     @Override
-    protected Parcelable[] getActiveNotifications() {
+    public Parcelable[] getActiveNotifications() {
         return new Parcelable[] { null };
     }
 
     @Override
-    protected int getSmallIconId() {
+    public int getSmallIconId() {
         return SMALL_ICON_ID;
     }
 }
diff --git a/browser/src/androidTest/java/androidx/browser/trusted/TrustedWebActivityBuilderTest.java b/browser/src/androidTest/java/androidx/browser/trusted/TrustedWebActivityBuilderTest.java
deleted file mode 100644
index d1531a1..0000000
--- a/browser/src/androidTest/java/androidx/browser/trusted/TrustedWebActivityBuilderTest.java
+++ /dev/null
@@ -1,119 +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.browser.trusted;
-
-import static androidx.browser.customtabs.TrustedWebUtils.EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY;
-import static androidx.browser.customtabs.testutil.TestUtil.getBrowserActivityWhenLaunched;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.browser.customtabs.CustomTabsSession;
-import androidx.browser.customtabs.CustomTabsSessionToken;
-import androidx.browser.customtabs.EnableComponentsTestRule;
-import androidx.browser.customtabs.TestActivity;
-import androidx.browser.customtabs.TestCustomTabsServiceSupportsTwas;
-import androidx.browser.customtabs.testutil.CustomTabConnectionRule;
-import androidx.browser.trusted.splashscreens.SplashScreenParamKey;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.MediumTest;
-import androidx.test.rule.ActivityTestRule;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Tests for {@link TrustedWebActivityBuilder}.
- */
-@RunWith(AndroidJUnit4.class)
-@MediumTest
-public class TrustedWebActivityBuilderTest {
-
-    @Rule
-    public final EnableComponentsTestRule mEnableComponents = new EnableComponentsTestRule(
-            TestActivity.class,
-            TestBrowser.class,
-            TestCustomTabsServiceSupportsTwas.class
-    );
-
-    @Rule
-    public final ActivityTestRule<TestActivity> mActivityTestRule =
-            new ActivityTestRule<>(TestActivity.class, false, true);
-
-    @Rule
-    public final CustomTabConnectionRule mConnectionRule = new CustomTabConnectionRule();
-
-    private TestActivity mActivity;
-    private CustomTabsSession mSession;
-
-    @Before
-    public void setUp() {
-        mActivity = mActivityTestRule.getActivity();
-        mSession = mConnectionRule.establishSessionBlocking(mActivity);
-    }
-
-    @Test
-    public void intentIsConstructedCorrectly() {
-        Uri url = Uri.parse("https://test.com/page");
-        int statusBarColor = 0xaabbcc;
-        List<String> additionalTrustedOrigins =
-                Arrays.asList("https://m.test.com", "https://test.org");
-
-        Bundle splashScreenParams = new Bundle();
-        int splashBgColor = 0x112233;
-        splashScreenParams.putInt(
-                SplashScreenParamKey.BACKGROUND_COLOR, splashBgColor);
-
-        final TrustedWebActivityBuilder builder =
-                new TrustedWebActivityBuilder(mActivity, url)
-                        .setStatusBarColor(statusBarColor)
-                        .setAdditionalTrustedOrigins(additionalTrustedOrigins)
-                        .setSplashScreenParams(splashScreenParams);
-        Intent intent =
-                getBrowserActivityWhenLaunched(new Runnable() {
-                    @Override
-                    public void run() {
-                        builder.launchActivity(mSession);
-                    }
-                }).getIntent();
-
-        assertTrue(intent.getBooleanExtra(EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY, false));
-        assertTrue(CustomTabsSessionToken.getSessionTokenFromIntent(intent)
-                .isAssociatedWith(mSession));
-        assertEquals(url, intent.getData());
-        assertEquals(statusBarColor, intent.getIntExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR, 0));
-        assertEquals(additionalTrustedOrigins, intent.getStringArrayListExtra(
-                TrustedWebActivityBuilder.EXTRA_ADDITIONAL_TRUSTED_ORIGINS));
-
-        Bundle splashScreenParamsReceived =
-                intent.getBundleExtra(TrustedWebActivityBuilder.EXTRA_SPLASH_SCREEN_PARAMS);
-
-        // No need to test every splash screen param: they are sent in as-is in provided Bundle.
-        assertEquals(splashBgColor, splashScreenParamsReceived.getInt(
-                SplashScreenParamKey.BACKGROUND_COLOR));
-    }
-}
diff --git a/browser/src/main/java/androidx/browser/customtabs/CustomTabColorSchemeParams.java b/browser/src/main/java/androidx/browser/customtabs/CustomTabColorSchemeParams.java
index d4f0592..cf4fbfa 100644
--- a/browser/src/main/java/androidx/browser/customtabs/CustomTabColorSchemeParams.java
+++ b/browser/src/main/java/androidx/browser/customtabs/CustomTabColorSchemeParams.java
@@ -119,7 +119,7 @@
          */
         @NonNull
         public Builder setToolbarColor(@ColorInt int color) {
-            mToolbarColor = color;
+            mToolbarColor = color | 0xff000000;
             return this;
         }
 
@@ -137,7 +137,7 @@
          */
         @NonNull
         public Builder setNavigationBarColor(@ColorInt int color) {
-            mNavigationBarColor = color;
+            mNavigationBarColor = color | 0xff000000;
             return this;
         }
 
diff --git a/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java b/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java
index 0cb25e5..b80beb4 100644
--- a/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java
+++ b/browser/src/main/java/androidx/browser/customtabs/CustomTabsIntent.java
@@ -355,21 +355,7 @@
          * Creates a {@link CustomTabsIntent.Builder} object associated with no
          * {@link CustomTabsSession}.
          */
-        public Builder() {
-            initialize(null, null);
-        }
-
-        /**
-         * Creates a {@link CustomTabsIntent.Builder} object associated with a given
-         * {@link CustomTabsSession.PendingSession}.
-         *
-         * {@see Builder(CustomTabsSession)}
-         * @hide
-         */
-        @RestrictTo(RestrictTo.Scope.LIBRARY)
-        public Builder(@Nullable CustomTabsSession.PendingSession session) {
-            initialize(null, session.getId());
-        }
+        public Builder() {}
 
         /**
          * Creates a {@link CustomTabsIntent.Builder} object associated with a given
@@ -382,16 +368,40 @@
          */
         public Builder(@Nullable CustomTabsSession session) {
             if (session != null) {
-                mIntent.setPackage(session.getComponentName().getPackageName());
-                initialize(session.getBinder(), session.getId());
-            } else {
-                initialize(null, null);
+                setSession(session);
             }
         }
 
-        private void initialize(@Nullable IBinder session, @Nullable PendingIntent sessionId) {
+        /**
+         * Associates the {@link Intent} with the given {@link CustomTabsSession}.
+         *
+         * Guarantees that the {@link Intent} will be sent to the same component as the one the
+         * session is associated with.
+         */
+        @NonNull
+        public Builder setSession(@NonNull CustomTabsSession session) {
+            mIntent.setPackage(session.getComponentName().getPackageName());
+            setSessionParameters(session.getBinder(), session.getId());
+            return this;
+        }
+
+        /**
+         * Associates the {@link Intent} with the given {@link CustomTabsSession.PendingSession}.
+         * Overrides the effect of {@link #setSession}.
+         *
+         * @hide
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        @NonNull
+        public Builder setPendingSession(@NonNull CustomTabsSession.PendingSession session) {
+            setSessionParameters(null, session.getId());
+            return this;
+        }
+
+        private void setSessionParameters(@Nullable IBinder binder,
+                @Nullable PendingIntent sessionId) {
             Bundle bundle = new Bundle();
-            BundleCompat.putBinder(bundle, EXTRA_SESSION, session);
+            BundleCompat.putBinder(bundle, EXTRA_SESSION, binder);
             if (sessionId != null) {
                 bundle.putParcelable(EXTRA_SESSION_ID, sessionId);
             }
@@ -718,6 +728,10 @@
          */
         @NonNull
         public CustomTabsIntent build() {
+            if (!mIntent.hasExtra(EXTRA_SESSION)) {
+                // The intent must have EXTRA_SESSION, even if it is null.
+                setSessionParameters(null, null);
+            }
             if (mMenuItems != null) {
                 mIntent.putParcelableArrayListExtra(CustomTabsIntent.EXTRA_MENU_ITEMS, mMenuItems);
             }
diff --git a/browser/src/main/java/androidx/browser/customtabs/TrustedWebUtils.java b/browser/src/main/java/androidx/browser/customtabs/TrustedWebUtils.java
index d287b43..7f50cbc 100644
--- a/browser/src/main/java/androidx/browser/customtabs/TrustedWebUtils.java
+++ b/browser/src/main/java/androidx/browser/customtabs/TrustedWebUtils.java
@@ -29,7 +29,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.WorkerThread;
-import androidx.browser.trusted.TrustedWebActivityBuilder;
+import androidx.browser.trusted.TrustedWebActivityIntentBuilder;
 import androidx.core.app.BundleCompat;
 import androidx.core.content.FileProvider;
 
@@ -134,9 +134,9 @@
      * Transfers the splash image to a Custom Tabs provider. The reading and decoding of the image
      * happens synchronously, so it's recommended to call this method on a worker thread.
      *
-     * This method should be called prior to {@link TrustedWebActivityBuilder#launchActivity}.
+     * This method should be called prior to launching the Activity.
      * Pass additional parameters, such as background color, using
-     * {@link TrustedWebActivityBuilder#setSplashScreenParams(Bundle)}.
+     * {@link TrustedWebActivityIntentBuilder#setSplashScreenParams(Bundle)}.
      *
      * @param context {@link Context} to use.
      * @param file {@link File} with the image.
@@ -169,8 +169,7 @@
      *                         associated with browser toolbar controls will be ignored.
      * @param uri The web page to launch as Trusted Web Activity.
      *
-     * @deprecated Use {@link TrustedWebActivityBuilder} and
-     * {@link TrustedWebActivityBuilder#launchActivity} instead.
+     * @deprecated Use {@link TrustedWebActivityIntentBuilder} instead.
      */
     @Deprecated
     public static void launchAsTrustedWebActivity(@NonNull Context context,
diff --git a/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityBuilder.java b/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityBuilder.java
deleted file mode 100644
index 9c456cd..0000000
--- a/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityBuilder.java
+++ /dev/null
@@ -1,184 +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.browser.trusted;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.browser.customtabs.CustomTabsSession;
-import androidx.browser.customtabs.TrustedWebUtils;
-import androidx.browser.trusted.splashscreens.SplashScreenParamKey;
-import androidx.core.content.ContextCompat;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Constructs and launches an intent to start a Trusted Web Activity (see {@link TrustedWebUtils}
- * for more details).
- */
-public class TrustedWebActivityBuilder {
-    /**
-     * Extra for the Trusted Web Activity launch Intent to specify a {@link Bundle} of parameters
-     * for the browser to use in constructing a splash screen.
-     *
-     * It is recommended to use {@link TrustedWebActivityBuilder} instead of manually piecing the
-     * Intent together.
-     */
-    @SuppressLint("ActionValue")
-    public static final String EXTRA_SPLASH_SCREEN_PARAMS =
-            "androidx.browser.trusted.EXTRA_SPLASH_SCREEN_PARAMS";
-
-    /**
-     * Extra for the Trusted Web Activity launch Intent to specify a list of origins for the
-     * browser to treat as trusted, in addition to the origin of the launching URL.
-     *
-     * It is recommended to use {@link TrustedWebActivityBuilder} instead of manually piecing the
-     * Intent together.
-     */
-    @SuppressLint("ActionValue")
-    public static final String EXTRA_ADDITIONAL_TRUSTED_ORIGINS =
-            "android.support.customtabs.extra.ADDITIONAL_TRUSTED_ORIGINS";
-
-    private final Context mContext;
-    private final Uri mUri;
-
-    @Nullable private Integer mStatusBarColor;
-    @Nullable private List<String> mAdditionalTrustedOrigins;
-    @Nullable private Bundle mSplashScreenParams;
-
-    /**
-     * Creates a Builder given the required parameters.
-     * @param context {@link Context} to use.
-     * @param uri The web page to launch as Trusted Web Activity.
-     */
-    public TrustedWebActivityBuilder(@NonNull Context context, @NonNull Uri uri) {
-        mContext = context;
-        mUri = uri;
-    }
-
-    /**
-     * Sets the status bar color to be seen while the Trusted Web Activity is running.
-     *
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    @NonNull
-    public TrustedWebActivityBuilder setStatusBarColor(@ColorInt int color) {
-        mStatusBarColor = color;
-        return this;
-    }
-
-    /**
-     * Sets a list of additional trusted origins that the user may navigate or be redirected to
-     * from the starting uri.
-     *
-     * For example, if the user starts at https://www.example.com/page1 and is redirected to
-     * https://m.example.com/page2, and both origins are associated with the calling application
-     * via the Digital Asset Links, then pass "https://www.example.com/page1" as uri and
-     * Arrays.asList("https://m.example.com") as additionalTrustedOrigins.
-     *
-     * Alternatively, use {@link CustomTabsSession#validateRelationship} to validate additional
-     * origins asynchronously, but that would delay launching the Trusted Web Activity.
-     */
-    @NonNull
-    public TrustedWebActivityBuilder setAdditionalTrustedOrigins(
-            @NonNull List<String> origins) {
-        mAdditionalTrustedOrigins = origins;
-        return this;
-    }
-
-    /**
-     * Sets the parameters of a splash screen shown while the web page is loading, such as
-     * background color. See {@link SplashScreenParamKey} for a list of supported parameters.
-     *
-     * To provide the image for the splash screen, use {@link TrustedWebUtils#transferSplashImage},
-     * prior to calling {@link #launchActivity} on the builder.
-     *
-     * It is recommended to also show the same splash screen in the app as soon as possible,
-     * prior to establishing a CustomTabConnection. The Trusted Web Activity provider should
-     * ensure seamless transition of the splash screen from the app onto the top of webpage
-     * being loaded.
-     *
-     * The splash screen will be removed on the first paint of the page, or when the page load
-     * fails.
-     */
-    @NonNull
-    public TrustedWebActivityBuilder setSplashScreenParams(@NonNull Bundle splashScreenParams) {
-        mSplashScreenParams = splashScreenParams;
-        return this;
-    }
-
-    /**
-     * Launches a Trusted Web Activity. Once it is launched, browser side implementations may
-     * have their own fallback behavior (e.g. showing the page in a custom tab UI with toolbar).
-     *
-     * @param session The {@link CustomTabsSession} to use for launching a Trusted Web Activity.
-     */
-    public void launchActivity(@NonNull CustomTabsSession session) {
-        if (session == null) {
-            throw new NullPointerException("CustomTabsSession is required for launching a TWA");
-        }
-
-        CustomTabsIntent.Builder intentBuilder = new CustomTabsIntent.Builder(session);
-        if (mStatusBarColor != null) {
-            // Toolbar color applies also to the status bar.
-            intentBuilder.setToolbarColor(mStatusBarColor);
-        }
-
-        Intent intent = intentBuilder.build().intent;
-        intent.setData(mUri);
-        intent.putExtra(TrustedWebUtils.EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY, true);
-        if (mAdditionalTrustedOrigins != null) {
-            intent.putExtra(EXTRA_ADDITIONAL_TRUSTED_ORIGINS,
-                    new ArrayList<>(mAdditionalTrustedOrigins));
-        }
-
-        if (mSplashScreenParams != null) {
-            intent.putExtra(EXTRA_SPLASH_SCREEN_PARAMS, mSplashScreenParams);
-        }
-        ContextCompat.startActivity(mContext, intent, null);
-    }
-
-    /**
-     * Returns the {@link Uri} to be launched with this Builder.
-     */
-    @NonNull
-    public Uri getUrl() {
-        return mUri;
-    }
-
-    /**
-     * Returns the color set via {@link #setStatusBarColor(int)} or {@code null} if not set.
-     *
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    @Nullable
-    @ColorInt
-    public Integer getStatusBarColor() {
-        return mStatusBarColor;
-    }
-}
diff --git a/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityIntentBuilder.java b/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityIntentBuilder.java
new file mode 100644
index 0000000..b87fea5
--- /dev/null
+++ b/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityIntentBuilder.java
@@ -0,0 +1,210 @@
+/*
+ * 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.browser.trusted;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.browser.customtabs.CustomTabColorSchemeParams;
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsSession;
+import androidx.browser.customtabs.TrustedWebUtils;
+import androidx.browser.trusted.splashscreens.SplashScreenParamKey;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Constructs an intent to start a Trusted Web Activity (see {@link TrustedWebUtils} for more
+ * details).
+ */
+public class TrustedWebActivityIntentBuilder {
+    /**
+     * Extra for the Trusted Web Activity launch Intent to specify a {@link Bundle} of parameters
+     * for the browser to use in constructing a splash screen.
+     *
+     * It is recommended to use {@link TrustedWebActivityIntentBuilder} instead of manually piecing
+     * the Intent together.
+     */
+    @SuppressLint("ActionValue")
+    public static final String EXTRA_SPLASH_SCREEN_PARAMS =
+            "androidx.browser.trusted.EXTRA_SPLASH_SCREEN_PARAMS";
+
+    /**
+     * Extra for the Trusted Web Activity launch Intent to specify a list of origins for the
+     * browser to treat as trusted, in addition to the origin of the launching URL.
+     *
+     * It is recommended to use {@link TrustedWebActivityIntentBuilder} instead of manually piecing
+     * the Intent together.
+     */
+    @SuppressLint("ActionValue")
+    public static final String EXTRA_ADDITIONAL_TRUSTED_ORIGINS =
+            "android.support.customtabs.extra.ADDITIONAL_TRUSTED_ORIGINS";
+
+    @NonNull private final Uri mUri;
+    @NonNull private final CustomTabsIntent.Builder mIntentBuilder = new CustomTabsIntent.Builder();
+
+    @Nullable private List<String> mAdditionalTrustedOrigins;
+    @Nullable private Bundle mSplashScreenParams;
+
+    /**
+     * Creates a Builder given the required parameters.
+     * @param uri The web page to launch as Trusted Web Activity.
+     */
+    public TrustedWebActivityIntentBuilder(@NonNull Uri uri) {
+        mUri = uri;
+    }
+
+    /**
+     * Sets the color applied to the toolbar and the status bar, see
+     * {@link CustomTabsIntent.Builder#setToolbarColor}.
+     *
+     * When a Trusted Web Activity is on the verified origin, the toolbar is hidden, so the color
+     * applies only to the status bar. When it's on an unverified origin, the toolbar is shown, and
+     * the color applies to both toolbar and status bar.
+     */
+    @NonNull
+    public TrustedWebActivityIntentBuilder setToolbarColor(@ColorInt int color) {
+        mIntentBuilder.setToolbarColor(color);
+        return this;
+    }
+
+    /**
+     * Sets the navigation bar color, see {@link CustomTabsIntent.Builder#setNavigationBarColor}.
+     */
+    @NonNull
+    public TrustedWebActivityIntentBuilder setNavigationBarColor(@ColorInt int color) {
+        mIntentBuilder.setNavigationBarColor(color);
+        return this;
+    }
+
+    /**
+     * Sets the color scheme, see {@link CustomTabsIntent.Builder#setColorScheme}.
+     * In Trusted Web Activities color scheme may effect such UI elements as info bars and context
+     * menus.
+     *
+     * @param colorScheme Must be one of {@link CustomTabsIntent#COLOR_SCHEME_SYSTEM},
+     * {@link CustomTabsIntent#COLOR_SCHEME_LIGHT}, and {@link CustomTabsIntent#COLOR_SCHEME_DARK}.
+     */
+    @NonNull
+    public TrustedWebActivityIntentBuilder setColorScheme(int colorScheme) {
+        mIntentBuilder.setColorScheme(colorScheme);
+        return this;
+    }
+
+    /**
+     * Sets {@link CustomTabColorSchemeParams} for the given color scheme.
+     * This allows, for example, to set two navigation bar colors - for light and dark scheme.
+     * Trusted Web Activity will automatically apply the correct color according to current system
+     * settings. For more details see {@link CustomTabsIntent.Builder#setColorSchemeParams}.
+     */
+    @NonNull
+    public TrustedWebActivityIntentBuilder setColorSchemeParams(int colorScheme,
+            @NonNull CustomTabColorSchemeParams params) {
+        mIntentBuilder.setColorSchemeParams(colorScheme, params);
+        return this;
+    }
+    /**
+     * Sets a list of additional trusted origins that the user may navigate or be redirected to
+     * from the starting uri.
+     *
+     * For example, if the user starts at https://www.example.com/page1 and is redirected to
+     * https://m.example.com/page2, and both origins are associated with the calling application
+     * via the Digital Asset Links, then pass "https://www.example.com/page1" as uri and
+     * Arrays.asList("https://m.example.com") as additionalTrustedOrigins.
+     *
+     * Alternatively, use {@link CustomTabsSession#validateRelationship} to validate additional
+     * origins asynchronously, but that would delay launching the Trusted Web Activity.
+     */
+    @NonNull
+    public TrustedWebActivityIntentBuilder setAdditionalTrustedOrigins(
+            @NonNull List<String> origins) {
+        mAdditionalTrustedOrigins = origins;
+        return this;
+    }
+
+    /**
+     * Sets the parameters of a splash screen shown while the web page is loading, such as
+     * background color. See {@link SplashScreenParamKey} for a list of supported parameters.
+     *
+     * To provide the image for the splash screen, use {@link TrustedWebUtils#transferSplashImage},
+     * prior to launching the intent.
+     *
+     * It is recommended to also show the same splash screen in the app as soon as possible,
+     * prior to establishing a CustomTabConnection. The Trusted Web Activity provider should
+     * ensure seamless transition of the splash screen from the app onto the top of webpage
+     * being loaded.
+     *
+     * The splash screen will be removed on the first paint of the page, or when the page load
+     * fails.
+     */
+    @NonNull
+    public TrustedWebActivityIntentBuilder setSplashScreenParams(
+            @NonNull Bundle splashScreenParams) {
+        mSplashScreenParams = splashScreenParams;
+        return this;
+    }
+
+    /**
+     * Builds the Intent.
+     *
+     * @param session The {@link CustomTabsSession} to use for launching a Trusted Web Activity.
+     */
+    @NonNull
+    public Intent build(@NonNull CustomTabsSession session) {
+        if (session == null) {
+            throw new NullPointerException("CustomTabsSession is required for launching a TWA");
+        }
+
+        mIntentBuilder.setSession(session);
+        Intent intent = mIntentBuilder.build().intent;
+        intent.setData(mUri);
+        intent.putExtra(TrustedWebUtils.EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY, true);
+        if (mAdditionalTrustedOrigins != null) {
+            intent.putExtra(EXTRA_ADDITIONAL_TRUSTED_ORIGINS,
+                    new ArrayList<>(mAdditionalTrustedOrigins));
+        }
+
+        if (mSplashScreenParams != null) {
+            intent.putExtra(EXTRA_SPLASH_SCREEN_PARAMS, mSplashScreenParams);
+        }
+        return intent;
+    }
+
+    /**
+     * Builds a {@link CustomTabsIntent} based on provided parameters.
+     * Can be useful for falling back to Custom Tabs when Trusted Web Activity providers are
+     * unavailable.
+     */
+    @NonNull
+    public CustomTabsIntent buildCustomTabsIntent() {
+        return mIntentBuilder.build();
+    }
+
+    /**
+     * Returns the {@link Uri} to be launched with this Builder.
+     */
+    @NonNull
+    public Uri getUrl() {
+        return mUri;
+    }
+}
diff --git a/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityService.java b/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityService.java
index efa1e34..4b08730 100644
--- a/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityService.java
+++ b/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityService.java
@@ -16,6 +16,7 @@
 
 package androidx.browser.trusted;
 
+import android.annotation.SuppressLint;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.Service;
@@ -35,6 +36,7 @@
 import android.support.customtabs.trusted.ITrustedWebActivityService;
 
 import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.browser.trusted.TrustedWebActivityServiceWrapper.ActiveNotificationsArgs;
@@ -49,7 +51,7 @@
 
 /**
  * The TrustedWebActivityService lives in a client app and serves requests from a Trusted Web
- * Activity provider. At present it only serves requests to display notifications.
+ * Activity provider. At present it only serves requests to do with notifications.
  * <p>
  * When the provider receives a notification from a scope that is associated with a Trusted Web
  * Activity client app, it will attempt to connect to a TrustedWebActivityService and forward calls.
@@ -60,7 +62,7 @@
  *
  * <pre>
  * <service
- *     android:name="android.support.customtabs.trusted.TrustedWebActivityService"
+ *     android:name="androidx.browser.trusted.TrustedWebActivityService"
  *     android:enabled="true"
  *     android:exported="true">
  *
@@ -77,42 +79,43 @@
  * The SMALL_ICON resource should point to a drawable to be used for the notification's small icon.
  * <p>
  * Alternatively for greater customization, TrustedWebActivityService can be extended and
- * {@link #onCreate}, {@link #getSmallIconId}, {@link #notifyNotificationWithChannel} and
- * {@link #cancelNotification} can be overridden. In this case the manifest entry should be updated
- * to point to the extending class.
+ * overridden. In this case the manifest entry should be updated to point to the extending class.
  * <p>
  * As this is an AIDL Service, calls to {@link #getSmallIconId},
  * {@link #notifyNotificationWithChannel} and {@link #cancelNotification} can occur on different
  * Binder threads, so overriding implementations need to be thread-safe.
  * <p>
  * For security, the TrustedWebActivityService will check that whatever connects to it is the
- * Trusted Web Activity provider that it was previously verified with. For testing,
- * {@link #setVerifiedProviderSynchronouslyForTesting} can be used to to allow connections from the
- * given package.
- *
- * @hide
+ * Trusted Web Activity provider that it was previously verified with (through
+ * {@link #setVerifiedProvider}).
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
 public class TrustedWebActivityService extends Service {
     /** An Intent Action used by the provider to find the TrustedWebActivityService or subclass. */
-    public static final String INTENT_ACTION =
+    @SuppressLint({
+            "ActionValue",  // This value was being used before being moved into AndroidX.
+            "ServiceName",  // This variable is an Action, but Metalava thinks it's a Service.
+    })
+    public static final String ACTION_TRUSTED_WEB_ACTIVITY_SERVICE =
             "android.support.customtabs.trusted.TRUSTED_WEB_ACTIVITY_SERVICE";
+
     /** The Android Manifest meta-data name to specify a small icon id to use. */
-    public static final String SMALL_ICON_META_DATA_NAME =
+    public static final String META_DATA_NAME_SMALL_ICON =
             "android.support.customtabs.trusted.SMALL_ICON";
 
+    /** The key to use to store a Bitmap to return from the {@link #getSmallIconBitmap()} method. */
+    public static final String KEY_SMALL_ICON_BITMAP =
+            "android.support.customtabs.trusted.SMALL_ICON_BITMAP";
+
     /** Used as a return value of {@link #getSmallIconId} when the icon is not provided. */
-    public static final int NO_ID = -1;
+    public static final int SMALL_ICON_NOT_SET = -1;
 
     private static final String PREFS_FILE = "TrustedWebActivityVerifiedProvider";
     private static final String PREFS_VERIFIED_PROVIDER = "Provider";
 
-    static final String KEY_SMALL_ICON_BITMAP =
-            "android.support.customtabs.trusted.SMALL_ICON_BITMAP";
-
     private NotificationManager mNotificationManager;
 
-    public int mVerifiedUid = -1;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    int mVerifiedUid = -1;
 
     private final ITrustedWebActivityService.Stub mBinder =
             new ITrustedWebActivityService.Stub() {
@@ -214,7 +217,7 @@
      * @param channelName The name of the notification channel to be used on Android O+.
      * @return Whether notifications are enabled.
      */
-    protected boolean areNotificationsEnabled(String channelName) {
+    public boolean areNotificationsEnabled(@NonNull String channelName) {
         ensureOnCreateCalled();
 
         if (!NotificationManagerCompat.from(this).areNotificationsEnabled()) return false;
@@ -238,8 +241,8 @@
      * @return Whether the notification was successfully displayed (the channel/app may be blocked
      *         by the user).
      */
-    protected boolean notifyNotificationWithChannel(String platformTag, int platformId,
-            Notification notification, String channelName) {
+    public boolean notifyNotificationWithChannel(@NonNull String platformTag, int platformId,
+            @NonNull Notification notification, @NonNull String channelName) {
         ensureOnCreateCalled();
 
         if (!NotificationManagerCompat.from(this).areNotificationsEnabled()) return false;
@@ -265,7 +268,7 @@
      * @param platformId The notification id, see
      *                   {@link NotificationManager#cancel(String, int)}.
      */
-    protected void cancelNotification(String platformTag, int platformId) {
+    public void cancelNotification(@NonNull String platformTag, int platformId) {
         ensureOnCreateCalled();
         mNotificationManager.cancel(platformTag, platformId);
     }
@@ -275,9 +278,11 @@
      * NotificationManager#getActiveNotifications. The default implementation does not work on
      * pre-Android M.
      * @return An array of StatusBarNotifications as Parcelables.
+     *
+     * @hide
      */
-
-    protected Parcelable[] getActiveNotifications() {
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    public Parcelable[] getActiveNotifications() {
         ensureOnCreateCalled();
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
             return NotificationApiHelperForM.getActiveNotifications(mNotificationManager);
@@ -285,10 +290,15 @@
         throw new IllegalStateException("getActiveNotifications cannot be called pre-M.");
     }
 
-    Bundle getSmallIconBitmap() {
+    /**
+     * Returns a Bundle containing a bitmap to be use as the small icon for any notifications.
+     * @return A Bundle that may contain a Bitmap contained with key {@link #KEY_SMALL_ICON_BITMAP}.
+     *         The bundle may be empty if the client app does not provide a small icon.
+     */
+    public @NonNull Bundle getSmallIconBitmap() {
         int id = getSmallIconId();
         Bundle bundle = new Bundle();
-        if (id == NO_ID) {
+        if (id == SMALL_ICON_NOT_SET) {
             return bundle;
         }
         bundle.putParcelable(KEY_SMALL_ICON_BITMAP,
@@ -298,35 +308,36 @@
 
     /**
      * Returns the Android resource id of a drawable to be used for the small icon of the
-     * notification. This is called by the provider as it is constructing the notification, so a
+     * notification. This is called by the provider as it is constructing the notification so a
      * complete notification can be passed to the client.
      *
-     * Default behaviour looks for meta-data with the name {@link #SMALL_ICON_META_DATA_NAME} in
+     * Default behaviour looks for meta-data with the name {@link #META_DATA_NAME_SMALL_ICON} in
      * service section of the manifest.
-     * @return A resource id for the small icon, or {@link #NO_ID} if not found.
+     * @return A resource id for the small icon, or {@link #SMALL_ICON_NOT_SET} if not found.
      */
-    protected int getSmallIconId() {
+    public int getSmallIconId() {
         try {
             ServiceInfo info = getPackageManager().getServiceInfo(
                     new ComponentName(this, getClass()), PackageManager.GET_META_DATA);
 
-            if (info.metaData == null) return NO_ID;
+            if (info.metaData == null) return SMALL_ICON_NOT_SET;
 
-            return info.metaData.getInt(SMALL_ICON_META_DATA_NAME, NO_ID);
+            return info.metaData.getInt(META_DATA_NAME_SMALL_ICON, SMALL_ICON_NOT_SET);
         } catch (PackageManager.NameNotFoundException e) {
             // Will only happen if the package provided (the one we are running in) is not
             // installed - so should never happen.
-            return NO_ID;
+            return SMALL_ICON_NOT_SET;
         }
     }
 
     @Override
-    public final IBinder onBind(Intent intent) {
+    @Nullable
+    public final IBinder onBind(@Nullable Intent intent) {
         return mBinder;
     }
 
     @Override
-    public final boolean onUnbind(Intent intent) {
+    public final boolean onUnbind(@Nullable Intent intent) {
         mVerifiedUid = -1;
 
         return super.onUnbind(intent);
@@ -335,6 +346,7 @@
     /**
      * Should *not* be called on UI Thread, as accessing Preferences may hit disk.
      */
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     static SharedPreferences getPreferences(Context context) {
         return context.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE);
     }
@@ -343,10 +355,9 @@
      * Sets (asynchronously) the package that this service will accept connections from.
      * @param context A context to be used to access SharedPreferences.
      * @param provider The package of the provider to accept connections from or null to clear.
-     * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public static final void setVerifiedProvider(final Context context, @Nullable String provider) {
+    public static final void setVerifiedProvider(final @NonNull Context context,
+            @Nullable String provider) {
         final String providerEmptyChecked =
                 (provider == null || provider.isEmpty()) ? null : provider;
 
diff --git a/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceConnectionManager.java b/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceConnectionManager.java
index d13c9dc..6dbcebe 100644
--- a/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceConnectionManager.java
+++ b/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceConnectionManager.java
@@ -33,8 +33,9 @@
 import android.support.customtabs.trusted.ITrustedWebActivityService;
 import android.util.Log;
 
+import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
 
 import java.util.Collections;
 import java.util.HashMap;
@@ -49,38 +50,31 @@
  * A TrustedWebActivityServiceConnectionManager will be used by a Trusted Web Activity provider and
  * takes care of connecting to and communicating with {@link TrustedWebActivityService}s.
  * <p>
- * Trusted Web Activity client apps are registered with the {@link #registerClient}, associating a
+ * Trusted Web Activity client apps are registered with {@link #registerClient}, associating a
  * package with an origin. There may be multiple packages associated with a single origin.
  * Note, the origins are essentially keys to a map of origin to package name - while they
  * semantically are web origins, they aren't used that way.
  * <p>
  * To interact with a {@link TrustedWebActivityService}, call {@link #execute}.
- *
- * @hide
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
 public class TrustedWebActivityServiceConnectionManager {
     private static final String TAG = "TWAConnectionManager";
     private static final String PREFS_FILE = "TrustedWebActivityVerifiedPackages";
 
     /**
      * A callback to be executed once a connection to a {@link TrustedWebActivityService} is open.
-     *
-     * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
     public interface ExecutionCallback {
         /**
-         * Is run when a connection is open.
-         * See {@link #execute} for more information.
+         * Is run when a connection is open. See {@link #execute} for more information.
          * @param service A {@link TrustedWebActivityServiceWrapper} wrapping the connected
          *                {@link TrustedWebActivityService}.
          *                It may be null if the connection failed.
          * @throws RemoteException May be thrown by {@link TrustedWebActivityServiceWrapper}'s
-         *                         methods.
-         *                         If the user does not want to catch them, they will be caught
-         *                         gracefully by {@link #execute}.
+         *                         methods. If the developer does not want to catch them, they will
+         *                         be caught gracefully by {@link #execute}.
          */
+        @SuppressLint("RethrowRemoteException")  // We're accepting RemoteExceptions not throwing.
         void onConnected(@Nullable TrustedWebActivityServiceWrapper service) throws RemoteException;
     }
 
@@ -143,16 +137,15 @@
     private static AtomicReference<SharedPreferences> sSharedPreferences = new AtomicReference<>();
 
     /**
-     * Gets the verified packages for the given origin. |origin| may be null, in which case this
-     * method call will just trigger caching the Preferences.
-     *
-     * This is safe to be called on any thread, however it may hit disk.
+     * Gets the verified packages for the given origin. This is safe to be called on any thread,
+     * however it may hit disk the first time it is called.
      *
      * @param context A Context to be used for accessing SharedPreferences.
      * @param origin The origin that was previously used with {@link #registerClient}.
      * @return A set of package names. This set is safe to be modified.
      */
-    public static Set<String> getVerifiedPackages(Context context, String origin) {
+    public static @NonNull Set<String> getVerifiedPackages(@NonNull Context context,
+            @NonNull String origin) {
         // Loading preferences is on the critical path for this class - we need to synchronously
         // inform the client whether or not an notification can be handled by a TWA.
         // I considered loading the preferences into a cache on a background thread when this class
@@ -164,31 +157,35 @@
         StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
 
         try {
-            if (sSharedPreferences.get() == null) {
-                sSharedPreferences.compareAndSet(null,
-                        context.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE));
-            }
+            ensurePreferencesOpened(context);
 
-            return origin == null ? null :
-                    new HashSet<>(sSharedPreferences.get().getStringSet(origin,
-                            Collections.<String>emptySet()));
+            return new HashSet<>(
+                    sSharedPreferences.get().getStringSet(origin, Collections.<String>emptySet()));
         } finally {
             StrictMode.setThreadPolicy(policy);
         }
     }
 
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    static void ensurePreferencesOpened(@NonNull Context context) {
+        if (sSharedPreferences.get() == null) {
+            sSharedPreferences.compareAndSet(null,
+                    context.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE));
+        }
+    }
+
     /**
      * Creates a TrustedWebActivityServiceConnectionManager.
      * @param context A Context used for accessing SharedPreferences.
      */
-    public TrustedWebActivityServiceConnectionManager(Context context) {
+    public TrustedWebActivityServiceConnectionManager(@NonNull Context context) {
         mContext = context.getApplicationContext();
 
         // Asynchronously try to load (and therefore cache) the preferences.
         AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
             @Override
             public void run() {
-                getVerifiedPackages(mContext, null);
+                ensurePreferencesOpened(context);
             }
         });
     }
@@ -222,9 +219,10 @@
      * <p>
      * To find a Service to connect to, this method attempts to resolve an
      * {@link Intent#ACTION_VIEW} Intent with the {@code scope} as data. The first of the resolved
-     * packages that registered (through {@link #registerClient}) to {@code origin} will be chosen.
-     * Finally, an Intent with the action {@link TrustedWebActivityService#INTENT_ACTION} will be
-     * used to find the Service.
+     * packages that is registered (through {@link #registerClient}) to {@code origin} will be
+     * chosen. Finally, an Intent with the action
+     * {@link TrustedWebActivityService#ACTION_TRUSTED_WEB_ACTIVITY_SERVICE} will be used to find
+     * the Service.
      * <p>
      * This method should be called on the UI thread.
      *
@@ -243,7 +241,9 @@
      * @return Whether a {@link TrustedWebActivityService} was found.
      */
     @SuppressLint("StaticFieldLeak")
-    public boolean execute(final Uri scope, String origin, final ExecutionCallback callback) {
+    @MainThread
+    public boolean execute(@NonNull final Uri scope, @NonNull String origin,
+            @NonNull final ExecutionCallback callback) {
         final WrappedCallback wrappedCallback = wrapCallback(callback);
 
         // If we have an existing connection, use it.
@@ -306,7 +306,8 @@
      *               to.
      * @return Whether a {@link TrustedWebActivityService} was found.
      */
-    public boolean serviceExistsForScope(Uri scope, String origin) {
+    @MainThread
+    public boolean serviceExistsForScope(@NonNull Uri scope, @NonNull String origin) {
         // If we have an existing connection, we can deal with the scope.
         if (mConnections.get(scope) != null) return true;
 
@@ -364,7 +365,8 @@
         // Find the TrustedWebActivityService within that package.
         Intent serviceResolutionIntent = new Intent();
         serviceResolutionIntent.setPackage(resolvedPackage);
-        serviceResolutionIntent.setAction(TrustedWebActivityService.INTENT_ACTION);
+        serviceResolutionIntent.setAction(
+                TrustedWebActivityService.ACTION_TRUSTED_WEB_ACTIVITY_SERVICE);
         ResolveInfo info = appContext.getPackageManager().resolveService(serviceResolutionIntent,
                 PackageManager.MATCH_ALL);
 
@@ -389,7 +391,8 @@
      * @param origin The origin for which the package is relevant.
      * @param clientPackage The packages to register.
      */
-    public static void registerClient(Context context, String origin, String clientPackage) {
+    public static void registerClient(@NonNull Context context, @NonNull String origin,
+            @NonNull String clientPackage) {
         Set<String> possiblePackages = getVerifiedPackages(context, origin);
         possiblePackages.add(clientPackage);
 
@@ -398,6 +401,4 @@
         editor.putStringSet(origin, possiblePackages);
         editor.apply();
     }
-
-    // TODO(peconn): Do we want to be able to unregister a client? To wipe all clients?
 }
diff --git a/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceWrapper.java b/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceWrapper.java
index 57cb37c..8e50a7a 100644
--- a/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceWrapper.java
+++ b/browser/src/main/java/androidx/browser/trusted/TrustedWebActivityServiceWrapper.java
@@ -25,12 +25,13 @@
 import android.os.RemoteException;
 import android.support.customtabs.trusted.ITrustedWebActivityService;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 
 /**
- * TrustedWebActivityServiceWrapper is used by a Trusted Web Activity provider app to wrap calls to
+ * TrustedWebActivityServiceWrapper is used by a Trusted Web Activity provider to wrap calls to
  * the {@link TrustedWebActivityService} in the client app.
  * All of these calls except {@link #getComponentName()} forward over IPC
  * to corresponding calls on {@link TrustedWebActivityService}, eg {@link #getSmallIconId()}
@@ -38,10 +39,7 @@
  * <p>
  * These IPC calls are synchronous, though the {@link TrustedWebActivityService} method may hit the
  * disk. Therefore it is recommended to call them on a background thread (without StrictMode).
- *
- * @hide
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
 public class TrustedWebActivityServiceWrapper {
     // Inputs.
     private static final String KEY_PLATFORM_TAG =
@@ -59,11 +57,16 @@
     private static final String KEY_NOTIFICATION_SUCCESS =
             "android.support.customtabs.trusted.NOTIFICATION_SUCCESS";
 
+    private static final String REMOTE_EXCEPTION_MESSAGE = "RemoteException while trying to "
+            + "communicate with the TrustedWebActivityService, this is probably because the "
+            + "service died while attempting to respond. Check to see if the service crashed for "
+            + "some reason.";
+
     private final ITrustedWebActivityService mService;
     private final ComponentName mComponentName;
 
-    TrustedWebActivityServiceWrapper(ITrustedWebActivityService service,
-            ComponentName componentName) {
+    TrustedWebActivityServiceWrapper(@NonNull ITrustedWebActivityService service,
+            @NonNull ComponentName componentName) {
         mService = service;
         mComponentName = componentName;
     }
@@ -72,11 +75,14 @@
      * Checks whether notifications are enabled.
      * @param channelName The name of the channel to check enabled status. Only used on Android O+.
      * @return Whether notifications or the notification channel is blocked for the client app.
-     * @throws RemoteException If the Service dies while responding to the request.
      */
-    public boolean areNotificationsEnabled(String channelName) throws RemoteException {
-        Bundle args = new NotificationsEnabledArgs(channelName).toBundle();
-        return ResultArgs.fromBundle(mService.areNotificationsEnabled(args)).success;
+    public boolean areNotificationsEnabled(@NonNull String channelName) {
+        try {
+            Bundle args = new NotificationsEnabledArgs(channelName).toBundle();
+            return ResultArgs.fromBundle(mService.areNotificationsEnabled(args)).success;
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failure when connecting to TrustedWebActivityService", e);
+        }
     }
 
     /**
@@ -87,27 +93,32 @@
      * @param channel The name of the channel in the Trusted Web Activity client app to display the
      *                notification on.
      * @return Whether notifications or the notification channel are blocked for the client app.
-     * @throws RemoteException If the Service dies while responding to the request.
      * @throws SecurityException If verification with the TrustedWebActivityService fails.
      */
-    public boolean notify(String platformTag, int platformId, Notification notification,
-            String channel) throws RemoteException, SecurityException {
-        Bundle args = new NotifyNotificationArgs(platformTag, platformId, notification, channel)
-                .toBundle();
-        return ResultArgs.fromBundle(mService.notifyNotificationWithChannel(args)).success;
+    public boolean notify(@NonNull String platformTag, int platformId,
+            @NonNull Notification notification, @NonNull String channel) {
+        try {
+            Bundle args = new NotifyNotificationArgs(platformTag, platformId, notification, channel)
+                    .toBundle();
+            return ResultArgs.fromBundle(mService.notifyNotificationWithChannel(args)).success;
+        } catch (RemoteException e) {
+            throw new RuntimeException(REMOTE_EXCEPTION_MESSAGE, e);
+        }
     }
 
     /**
      * Requests a notification be cancelled.
      * @param platformTag The tag to identify the notification.
      * @param platformId The id to identify the notification.
-     * @throws RemoteException If the Service dies while responding to the request.
      * @throws SecurityException If verification with the TrustedWebActivityService fails.
      */
-    public void cancel(String platformTag, int platformId)
-            throws RemoteException, SecurityException {
-        Bundle args = new CancelNotificationArgs(platformTag, platformId).toBundle();
-        mService.cancelNotification(args);
+    public void cancel(@NonNull String platformTag, int platformId) {
+        try {
+            Bundle args = new CancelNotificationArgs(platformTag, platformId).toBundle();
+            mService.cancelNotification(args);
+        } catch (RemoteException e) {
+            throw new RuntimeException(REMOTE_EXCEPTION_MESSAGE, e);
+        }
     }
 
     /**
@@ -115,47 +126,57 @@
      * Android M and above.
      * @return An StatusBarNotification[] as a Parcelable[]. This is so this code can compile for
      *         Jellybean (even if it must not be called for pre-Marshmallow).
-     * @throws RemoteException If the Service dies while responding to the request.
      * @throws SecurityException If verification with the TrustedWebActivityService fails.
      * @throws IllegalStateException If called on Android pre-M.
      *
-     * TODO(peconn): Figure out want to handle the return type before making this public.
+     * @hide
      */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
     @RequiresApi(Build.VERSION_CODES.M)
-    public Parcelable[] getActiveNotifications()
-            throws RemoteException, SecurityException, IllegalStateException {
-        Bundle notifications = mService.getActiveNotifications();
-        return ActiveNotificationsArgs.fromBundle(notifications).notifications;
+    public Parcelable[] getActiveNotifications() {
+        try {
+            Bundle notifications = mService.getActiveNotifications();
+            return ActiveNotificationsArgs.fromBundle(notifications).notifications;
+        } catch (RemoteException e) {
+            throw new RuntimeException(REMOTE_EXCEPTION_MESSAGE, e);
+        }
     }
 
     /**
      * Requests an Android resource id to be used for the notification small icon.
      * @return An Android resource id for the notification small icon. -1 if non found.
-     * @throws RemoteException If the Service dies while responding to the request.
      * @throws SecurityException If verification with the TrustedWebActivityService fails.
      */
-    public int getSmallIconId() throws RemoteException, SecurityException {
-        return mService.getSmallIconId();
+    public int getSmallIconId() {
+        try {
+            return mService.getSmallIconId();
+        } catch (RemoteException e) {
+            throw new RuntimeException(REMOTE_EXCEPTION_MESSAGE, e);
+        }
     }
 
     /**
      * Requests a bitmap of a small icon to be used for the notification
      * small icon. The bitmap is decoded on the side of Trusted Web Activity client using
      * the resource id from {@link TrustedWebActivityService#getSmallIconId}.
-     * @return {@link SmallIconData} with both an id and a bitmap
-     * @throws RemoteException If the Service dies while responding to the request.
+     * @return A {@link Bitmap} to be used for the small icon.
      * @throws SecurityException If verification with the TrustedWebActivityService fails.
      */
     @Nullable
-    public Bitmap getSmallIconBitmap() throws RemoteException, SecurityException {
-        return mService.getSmallIconBitmap()
-                .getParcelable(TrustedWebActivityService.KEY_SMALL_ICON_BITMAP);
+    public Bitmap getSmallIconBitmap() {
+        try {
+            return mService.getSmallIconBitmap()
+                    .getParcelable(TrustedWebActivityService.KEY_SMALL_ICON_BITMAP);
+        } catch (RemoteException e) {
+            throw new RuntimeException(REMOTE_EXCEPTION_MESSAGE, e);
+        }
     }
 
     /**
      * Gets the {@link ComponentName} of the connected Trusted Web Activity client app.
      * @return The Trusted Web Activity client app component name.
      */
+    @NonNull
     public ComponentName getComponentName() {
         return mComponentName;
     }
diff --git a/browser/src/main/java/androidx/browser/trusted/splashscreens/SplashScreenParamKey.java b/browser/src/main/java/androidx/browser/trusted/splashscreens/SplashScreenParamKey.java
index 543d966..b91f786 100644
--- a/browser/src/main/java/androidx/browser/trusted/splashscreens/SplashScreenParamKey.java
+++ b/browser/src/main/java/androidx/browser/trusted/splashscreens/SplashScreenParamKey.java
@@ -18,13 +18,13 @@
 
 import android.os.Bundle;
 
-import androidx.browser.trusted.TrustedWebActivityBuilder;
+import androidx.browser.trusted.TrustedWebActivityIntentBuilder;
 
 /**
  * The keys of the entries in the {@link Bundle} passed to
- * {@link TrustedWebActivityBuilder#setSplashScreenParams}. This Bundle can also be assembled
+ * {@link TrustedWebActivityIntentBuilder#setSplashScreenParams}. This Bundle can also be assembled
  * manually and added to the launch Intent as an extra with the key
- * {@link TrustedWebActivityBuilder#EXTRA_SPLASH_SCREEN_PARAMS}.
+ * {@link TrustedWebActivityIntentBuilder#EXTRA_SPLASH_SCREEN_PARAMS}.
  */
 public final class SplashScreenParamKey {
     /**
diff --git a/browser/src/androidTest/java/androidx/browser/customtabs/CustomTabColorSchemeParamsTest.java b/browser/src/test/java/androidx/browser/customtabs/CustomTabColorSchemeParamsTest.java
similarity index 90%
rename from browser/src/androidTest/java/androidx/browser/customtabs/CustomTabColorSchemeParamsTest.java
rename to browser/src/test/java/androidx/browser/customtabs/CustomTabColorSchemeParamsTest.java
index 9d9bd11..d7cd6fa 100644
--- a/browser/src/androidTest/java/androidx/browser/customtabs/CustomTabColorSchemeParamsTest.java
+++ b/browser/src/test/java/androidx/browser/customtabs/CustomTabColorSchemeParamsTest.java
@@ -20,14 +20,17 @@
 import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 import android.content.Intent;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.internal.DoNotInstrument;
 
 
 /**
@@ -35,7 +38,8 @@
  * In particular, for {@link CustomTabsIntent.Builder#setColorSchemeParams} and
  * {@link CustomTabsIntent#getColorSchemeParams}
  */
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
 @SmallTest
 public class CustomTabColorSchemeParamsTest {
     @Test
@@ -211,17 +215,26 @@
                 .build()
                 .intent;
 
-        assertEquals(defaultToolbarColor,
+        assertColorEquals(defaultToolbarColor,
                 intent.getIntExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR, 0));
-        assertEquals(defaultSecondaryToolbarColor,
+        assertColorEquals(defaultSecondaryToolbarColor,
                 intent.getIntExtra(CustomTabsIntent.EXTRA_SECONDARY_TOOLBAR_COLOR, 0));
     }
 
 
     private void assertSchemeParamsEqual(CustomTabColorSchemeParams params1,
             CustomTabColorSchemeParams params2) {
-        assertEquals(params1.toolbarColor, params2.toolbarColor);
-        assertEquals(params1.secondaryToolbarColor, params2.secondaryToolbarColor);
-        assertEquals(params1.navigationBarColor, params2.navigationBarColor);
+        assertColorEquals(params1.toolbarColor, params2.toolbarColor);
+        assertColorEquals(params1.secondaryToolbarColor, params2.secondaryToolbarColor);
+        assertColorEquals(params1.navigationBarColor, params2.navigationBarColor);
+    }
+
+    // Compares colors ignoring alpha
+    private void assertColorEquals(@Nullable Integer color1, @Nullable Integer color2) {
+        if (color1 == null) {
+            assertNull(color2);
+        } else {
+            assertEquals(color1 & 0xffffff, color2 & 0xffffff);
+        }
     }
 }
diff --git a/browser/src/androidTest/java/androidx/browser/customtabs/CustomTabsIntentTest.java b/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java
similarity index 64%
rename from browser/src/androidTest/java/androidx/browser/customtabs/CustomTabsIntentTest.java
rename to browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java
index 3d65e75..e8a9654 100644
--- a/browser/src/androidTest/java/androidx/browser/customtabs/CustomTabsIntentTest.java
+++ b/browser/src/test/java/androidx/browser/customtabs/CustomTabsIntentTest.java
@@ -29,16 +29,22 @@
 
 import androidx.annotation.ColorRes;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.internal.DoNotInstrument;
+import org.robolectric.shadows.ShadowPendingIntent;
 
 /**
  * Tests for CustomTabsIntent.
  */
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+// minSdk For Bundle#getBinder
+@Config(minSdk = Build.VERSION_CODES.JELLY_BEAN_MR2, shadows = ShadowPendingIntent.class)
 @SmallTest
 public class CustomTabsIntentTest {
 
@@ -50,10 +56,6 @@
         assertNull(customTabsIntent.startAnimationBundle);
 
         assertEquals(Intent.ACTION_VIEW, intent.getAction());
-        assertTrue(intent.hasExtra(CustomTabsIntent.EXTRA_SESSION));
-        if (Build.VERSION.SDK_INT >= 18) {
-            assertNull(intent.getExtras().getBinder(CustomTabsIntent.EXTRA_SESSION));
-        }
         assertNull(intent.getComponent());
     }
 
@@ -113,4 +115,49 @@
             assertEquals(value, intent.getIntExtra(CustomTabsIntent.EXTRA_COLOR_SCHEME, -1));
         }
     }
+
+    @Test
+    public void hasNullSessionExtra_WhenBuiltWithDefaultConstructor() {
+        Intent intent = new CustomTabsIntent.Builder().build().intent;
+        assertNullSessionInExtras(intent);
+    }
+
+    @Test
+    public void hasNullSessionExtra_WhenBuiltWithNullSession() {
+        CustomTabsSession session = null;
+        Intent intent = new CustomTabsIntent.Builder(session).build().intent;
+        assertNullSessionInExtras(intent);
+    }
+
+    @Test
+    public void putsSessionBinderAndId_IfSuppliedInConstructor() {
+        CustomTabsSession session = TestUtil.makeMockSession();
+        Intent intent = new CustomTabsIntent.Builder(session).build().intent;
+        assertEquals(session.getBinder(),
+                intent.getExtras().getBinder(CustomTabsIntent.EXTRA_SESSION));
+        assertEquals(session.getId(), intent.getParcelableExtra(CustomTabsIntent.EXTRA_SESSION_ID));
+    }
+
+    @Test
+    public void putsSessionBinderAndId_IfSuppliedInSetter() {
+        CustomTabsSession session = TestUtil.makeMockSession();
+        Intent intent = new CustomTabsIntent.Builder().setSession(session).build().intent;
+        assertEquals(session.getBinder(),
+                intent.getExtras().getBinder(CustomTabsIntent.EXTRA_SESSION));
+        assertEquals(session.getId(), intent.getParcelableExtra(CustomTabsIntent.EXTRA_SESSION_ID));
+    }
+
+    @Test
+    public void putsPendingSessionId() {
+        CustomTabsSession.PendingSession pendingSession = TestUtil.makeMockPendingSession();
+        Intent intent = new CustomTabsIntent.Builder().setPendingSession(pendingSession).build()
+                .intent;
+        assertEquals(pendingSession.getId(),
+                intent.getParcelableExtra(CustomTabsIntent.EXTRA_SESSION_ID));
+    }
+
+    private void assertNullSessionInExtras(Intent intent) {
+        assertTrue(intent.hasExtra(CustomTabsIntent.EXTRA_SESSION));
+        assertNull(intent.getExtras().getBinder(CustomTabsIntent.EXTRA_SESSION));
+    }
 }
diff --git a/browser/src/test/java/androidx/browser/customtabs/TestUtil.java b/browser/src/test/java/androidx/browser/customtabs/TestUtil.java
new file mode 100644
index 0000000..d5316fd
--- /dev/null
+++ b/browser/src/test/java/androidx/browser/customtabs/TestUtil.java
@@ -0,0 +1,60 @@
+/*
+ * 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.browser.customtabs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.support.customtabs.ICustomTabsCallback;
+import android.support.customtabs.ICustomTabsService;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Utilities for unit testing Custom Tabs.
+ */
+public class TestUtil {
+
+    @NonNull
+    public static CustomTabsSession makeMockSession() {
+        return new CustomTabsSession(mock(ICustomTabsService.class),
+                mock(ICustomTabsCallback.class), new ComponentName("", ""),
+                makeMockPendingIntent());
+    }
+
+    @NonNull
+    public static CustomTabsSession.PendingSession makeMockPendingSession() {
+        return new CustomTabsSession.PendingSession(
+                mock(CustomTabsCallback.class), makeMockPendingIntent());
+    }
+
+    @NonNull
+    private static PendingIntent makeMockPendingIntent() {
+        return PendingIntent.getBroadcast(mock(Context.class), 0, new Intent(), 0);
+    }
+
+    public static void assertIntentHasSession(@NonNull Intent intent,
+            @NonNull CustomTabsSession session) {
+        assertEquals(session.getBinder(), intent.getExtras().getBinder(
+                CustomTabsIntent.EXTRA_SESSION));
+        assertEquals(session.getId(), intent.getParcelableExtra(CustomTabsIntent.EXTRA_SESSION_ID));
+    }
+}
diff --git a/browser/src/test/java/androidx/browser/trusted/TrustedWebActivityIntentBuilderTest.java b/browser/src/test/java/androidx/browser/trusted/TrustedWebActivityIntentBuilderTest.java
new file mode 100644
index 0000000..c86da1d9
--- /dev/null
+++ b/browser/src/test/java/androidx/browser/trusted/TrustedWebActivityIntentBuilderTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.browser.trusted;
+
+import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK;
+import static androidx.browser.customtabs.TrustedWebUtils.EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+
+import androidx.browser.customtabs.CustomTabColorSchemeParams;
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsSession;
+import androidx.browser.customtabs.TestUtil;
+import androidx.browser.trusted.splashscreens.SplashScreenParamKey;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link TrustedWebActivityIntentBuilder}.
+ */
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+// minSdk For Bundle#getBinder
+@Config(minSdk = Build.VERSION_CODES.JELLY_BEAN_MR2)
+@SmallTest
+public class TrustedWebActivityIntentBuilderTest {
+
+    @Test
+    public void intentIsConstructedCorrectly() {
+        Uri url = Uri.parse("https://test.com/page");
+        int toolbarColor = 0xffaabbcc;
+        int navigationBarColor = 0xffccaabb;
+        List<String> additionalTrustedOrigins =
+                Arrays.asList("https://m.test.com", "https://test.org");
+
+        Bundle splashScreenParams = new Bundle();
+        int splashBgColor = 0x112233;
+        splashScreenParams.putInt(
+                SplashScreenParamKey.BACKGROUND_COLOR, splashBgColor);
+
+        CustomTabColorSchemeParams colorSchemeParams = new CustomTabColorSchemeParams.Builder()
+                    .setToolbarColor(0xff112233).build();
+
+        CustomTabsSession session = TestUtil.makeMockSession();
+
+        Intent intent = new TrustedWebActivityIntentBuilder(url)
+                        .setToolbarColor(toolbarColor)
+                        .setNavigationBarColor(navigationBarColor)
+                        .setColorScheme(COLOR_SCHEME_DARK)
+                        .setColorSchemeParams(COLOR_SCHEME_DARK, colorSchemeParams)
+                        .setAdditionalTrustedOrigins(additionalTrustedOrigins)
+                        .setSplashScreenParams(splashScreenParams)
+                        .build(session);
+
+        assertTrue(intent.getBooleanExtra(EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY, false));
+        TestUtil.assertIntentHasSession(intent, session);
+        assertEquals(url, intent.getData());
+        assertEquals(toolbarColor, intent.getIntExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR, 0));
+        assertEquals(navigationBarColor, intent.getIntExtra(
+                CustomTabsIntent.EXTRA_NAVIGATION_BAR_COLOR, 0));
+        assertEquals(additionalTrustedOrigins, intent.getStringArrayListExtra(
+                TrustedWebActivityIntentBuilder.EXTRA_ADDITIONAL_TRUSTED_ORIGINS));
+        assertEquals(COLOR_SCHEME_DARK, intent.getIntExtra(CustomTabsIntent.EXTRA_COLOR_SCHEME,
+                -1));
+        assertEquals(colorSchemeParams.toolbarColor,
+                CustomTabsIntent.getColorSchemeParams(intent, COLOR_SCHEME_DARK).toolbarColor);
+
+        Bundle splashScreenParamsReceived =
+                intent.getBundleExtra(TrustedWebActivityIntentBuilder.EXTRA_SPLASH_SCREEN_PARAMS);
+
+        // No need to test every splash screen param: they are sent in as-is in provided Bundle.
+        assertEquals(splashBgColor, splashScreenParamsReceived.getInt(
+                SplashScreenParamKey.BACKGROUND_COLOR));
+    }
+}
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 59996ce..c51fd30 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -58,11 +58,6 @@
     }
 }
 
-sourceSets {
-    main.java.srcDirs += "${supportRootFolder}/benchmark/gradle-plugin/src/main/kotlin"
-    main.resources.srcDirs += "${supportRootFolder}/benchmark/gradle-plugin/src/main/resources"
-}
-
 dependencies {
     implementation build_libs.gradle
     implementation build_libs.error_prone_gradle
@@ -73,3 +68,18 @@
     testImplementation "junit:junit:4.12"
 }
 
+apply plugin: "java-gradle-plugin"
+
+sourceSets {
+    main.java.srcDirs += "${supportRootFolder}/benchmark/gradle-plugin/src/main/kotlin"
+    main.resources.srcDirs += "${supportRootFolder}/benchmark/gradle-plugin/src/main/resources"
+}
+
+gradlePlugin {
+    plugins {
+        benchmark {
+            id = 'androidx.benchmark'
+            implementationClass = 'androidx.benchmark.gradle.BenchmarkPlugin'
+        }
+    }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
index 665df4f..35fedcb 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
@@ -16,8 +16,6 @@
 
 package androidx.build
 
-import androidx.benchmark.gradle.LockClocksTask
-import androidx.benchmark.gradle.UnlockClocksTask
 import androidx.build.SupportConfig.BENCHMARK_INSTRUMENTATION_RUNNER
 import androidx.build.SupportConfig.BUILD_TOOLS_VERSION
 import androidx.build.SupportConfig.COMPILE_SDK_VERSION
@@ -277,6 +275,7 @@
                         "verifyDependencyVersions" == task.name ||
                         "reportLibraryMetrics" == task.name ||
                         CREATE_STUB_API_JAR_TASK == task.name ||
+                        BUILD_ON_SERVER_TASK == task.name ||
                         ("lintDebug" == task.name &&
                         !project.rootProject.hasProperty(USE_MAX_DEP_VERSIONS))) {
                     buildOnServerTask.dependsOn(task)
@@ -315,14 +314,6 @@
             CheckSameVersionLibraryGroupsTask::class.java)
         buildOnServerTask.dependsOn(checkSameVersionLibraryGroupsTask)
 
-        val adbPath = "${getSdkPath(SupportConfig.getSupportRoot(project)).path}/platform-tools/adb"
-        tasks.register("lockClocks", LockClocksTask::class.java).configure {
-            it.adbPath.set(adbPath)
-        }
-        tasks.register("unlockClocks", UnlockClocksTask::class.java).configure {
-            it.adbPath.set(adbPath)
-        }
-
         AffectedModuleDetector.configure(gradle, this)
 
         // If useMaxDepVersions is set, iterate through all the project and substitute any androidx
@@ -370,6 +361,14 @@
         defaultConfig.targetSdkVersion(TARGET_SDK_VERSION)
         defaultConfig.testInstrumentationRunner =
             if (project.isBenchmark()) BENCHMARK_INSTRUMENTATION_RUNNER else INSTRUMENTATION_RUNNER
+
+        // 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
+        // instrumentation runner argument is passed raw to the `am instrument` command.
+        // NOTE - instrumentation args aren't respected by CI - window animations are
+        // disabled there separately
+        defaultConfig.testInstrumentationRunnerArgument("thisisignored",
+            "thisisignored --no-window-animation")
         testOptions.unitTests.isReturnDefaultValues = true
 
         defaultConfig.minSdkVersion(DEFAULT_MIN_SDK_VERSION)
@@ -632,8 +631,7 @@
 
 fun Project.isBenchmark(): Boolean {
     // benchmark convention is to end name with "-benchmark"
-    // Note: also match benchmark/src/androidTest, so it gets the BENCHMARK_INSTRUMENTATION_RUNNER
-    return name.endsWith("-benchmark") || name == "benchmark"
+    return name.endsWith("-benchmark")
 }
 
 fun Project.hideJavadocTask() {
diff --git a/buildSrc/src/main/kotlin/androidx/build/CreateLibraryBuildInfoFileTask.kt b/buildSrc/src/main/kotlin/androidx/build/CreateLibraryBuildInfoFileTask.kt
index f4b47a4..07f598a 100644
--- a/buildSrc/src/main/kotlin/androidx/build/CreateLibraryBuildInfoFileTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/CreateLibraryBuildInfoFileTask.kt
@@ -16,6 +16,7 @@
 
 package androidx.build
 
+import androidx.build.gmaven.GMavenVersionChecker
 import androidx.build.jetpad.LibraryBuildInfoFile
 import org.gradle.api.DefaultTask
 import org.gradle.api.artifacts.ProjectDependency
@@ -91,6 +92,7 @@
         val checks = ArrayList<LibraryBuildInfoFile.Check>()
         libraryBuildInfoFile.checks = checks
         val publishedProjects = project.getProjectsMap()
+        val versionChecker = project.property("versionChecker") as GMavenVersionChecker
         project.configurations.filter {
             /* Ignore test configuration dependencies */
             !it.name.contains("test", ignoreCase = true)
@@ -110,6 +112,15 @@
                         androidXPublishedDependency.groupId = dep.group.toString()
                         androidXPublishedDependency.version = dep.version.toString()
                         androidXPublishedDependency.isTipOfTree = dep is ProjectDependency
+                        // Check GMaven to confirm that the pinned dependency is not an unreleased
+                        // version
+                        if (!androidXPublishedDependency.isTipOfTree &&
+                            !versionChecker.isReleased(
+                                    androidXPublishedDependency.groupId,
+                                    androidXPublishedDependency.artifactId,
+                                    androidXPublishedDependency.version)) {
+                                androidXPublishedDependency.isTipOfTree = true
+                        }
                         addDependencyToListIfNotAlreadyAdded(
                             libraryDependencies,
                             androidXPublishedDependency
diff --git a/buildSrc/src/main/kotlin/androidx/build/Jetify.kt b/buildSrc/src/main/kotlin/androidx/build/Jetify.kt
index 25b8e8b..ec84188 100644
--- a/buildSrc/src/main/kotlin/androidx/build/Jetify.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/Jetify.kt
@@ -30,6 +30,7 @@
     "m2repository/androidx/arch/**",
     "m2repository/androidx/arch/core/**",
     "m2repository/androidx/asynclayoutinflater/**",
+    "m2repository/androidx/benchmark/**",
     "m2repository/androidx/biometric/**",
     "m2repository/androidx/browser/**",
     "m2repository/androidx/camera/**",
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
index 3d62b43..e0ecf75 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
@@ -23,7 +23,7 @@
     val ACTIVITY = LibraryGroup("androidx.activity")
     val ADS = LibraryGroup("androidx.ads", false)
     val ANIMATION = LibraryGroup("androidx.animation", false)
-    val ANNOTATION = LibraryGroup("androidx.annotation")
+    val ANNOTATION = LibraryGroup("androidx.annotation", false)
     val APPCOMPAT = LibraryGroup("androidx.appcompat", false)
     val ARCH_CORE = LibraryGroup("androidx.arch.core")
     val ASYNCLAYOUTINFLATER = LibraryGroup("androidx.asynclayoutinflater")
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index bfd5d87..9dacda3 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -25,6 +25,7 @@
     val ANIMATION = Version("1.0.0-alpha01")
     val ANIMATION_TESTING = Version("1.1.0-alpha01")
     val ANNOTATION = Version("1.2.0-alpha01")
+    val ANNOTATION_EXPERIMENTAL = Version("1.0.0-alpha01")
     val APPCOMPAT = Version("1.2.0-alpha01")
     val ARCH_CORE = Version("2.2.0-alpha01")
     val ARCH_CORE_TESTING = ARCH_CORE
@@ -33,7 +34,7 @@
     val AUTOFILL = Version("1.0.0-alpha02")
     val BENCHMARK = Version("1.0.0-alpha04")
     val BIOMETRIC = Version("1.0.0-beta01")
-    val BROWSER = Version("1.2.0-alpha05")
+    val BROWSER = Version("1.2.0-alpha07")
     val CAMERA = Version("1.0.0-alpha04")
     val CAMERA_EXTENSIONS = Version("1.0.0-alpha01")
     val CAMERA_VIEW = Version("1.0.0-alpha01")
@@ -57,7 +58,7 @@
     val ENTERPRISE = Version("1.0.0-alpha03")
     val EXIFINTERFACE = Version("1.1.0-beta02")
     val FRAGMENT = Version("1.2.0-alpha02")
-    val FUTURES = Version("1.0.0-beta02")
+    val FUTURES = Version("1.0.0-rc01")
     val GRIDLAYOUT = Version("1.1.0-alpha01")
     val HEIFWRITER = Version("1.1.0-alpha01")
     val INSPECTION = Version("1.0.0-alpha01")
@@ -109,7 +110,7 @@
     val VECTORDRAWABLE_ANIMATED = Version("1.2.0-alpha01")
     val VERSIONED_PARCELABLE = Version("1.2.0-alpha01")
     val VIEWPAGER = Version("1.1.0-alpha01")
-    val VIEWPAGER2 = Version("1.0.0-beta02")
+    val VIEWPAGER2 = Version("1.0.0-beta03")
     val WEAR = Version("1.1.0-alpha01")
     val WEBKIT = Version("1.1.0-alpha02")
     val WORK = Version("2.3.0-alpha01")
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index 4046931..6c5bd2c 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -34,8 +34,9 @@
     prebuilts(LibraryGroups.ARCH_CORE, "2.1.0-rc01")
     prebuilts(LibraryGroups.ASYNCLAYOUTINFLATER, "1.0.0")
     prebuilts(LibraryGroups.AUTOFILL, "1.0.0-alpha01")
+    prebuilts(LibraryGroups.BENCHMARK, "benchmark", "1.0.0-alpha03")
+    ignore(LibraryGroups.BENCHMARK.group, "benchmark-common")
     ignore(LibraryGroups.BENCHMARK.group, "benchmark-gradle-plugin")
-    prebuilts(LibraryGroups.BENCHMARK, "1.0.0-alpha03")
     prebuilts(LibraryGroups.BIOMETRIC, "biometric", "1.0.0-alpha04")
     prebuilts(LibraryGroups.BROWSER, "1.0.0")
     ignore(LibraryGroups.CAMERA.group, "camera-view")
@@ -118,15 +119,15 @@
     prebuilts(LibraryGroups.TVPROVIDER, "1.0.0")
     prebuilts(LibraryGroups.VECTORDRAWABLE, "1.1.0-rc01")
     prebuilts(LibraryGroups.VECTORDRAWABLE, "vectordrawable-animated", "1.1.0-rc01")
-    prebuilts(LibraryGroups.VERSIONEDPARCELABLE, "1.1.0-rc01")
+    prebuilts(LibraryGroups.VERSIONEDPARCELABLE, "1.1.0")
     prebuilts(LibraryGroups.VIEWPAGER, "1.0.0")
-    prebuilts(LibraryGroups.VIEWPAGER2, "1.0.0-beta01")
+    prebuilts(LibraryGroups.VIEWPAGER2, "1.0.0-beta02")
     prebuilts(LibraryGroups.WEAR, "1.0.0")
             .addStubs("wear/wear_stubs/com.google.android.wearable-stubs.jar")
     prebuilts(LibraryGroups.WEBKIT, "1.1.0-alpha01")
     ignore(LibraryGroups.WORK.group, "work-gcm")
     ignore(LibraryGroups.WORK.group, "work-foreground")
-    prebuilts(LibraryGroups.WORK, "2.2.0-beta02")
+    prebuilts(LibraryGroups.WORK, "2.2.0-rc01")
     default(Ignore)
 }
 
diff --git a/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt b/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt
index 34bd1bd..f9b32b8d 100644
--- a/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SupportConfig.kt
@@ -23,7 +23,7 @@
 object SupportConfig {
     const val DEFAULT_MIN_SDK_VERSION = 14
     const val INSTRUMENTATION_RUNNER = "androidx.test.runner.AndroidJUnitRunner"
-    const val BENCHMARK_INSTRUMENTATION_RUNNER = "androidx.benchmark.AndroidBenchmarkRunner"
+    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/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 2a3e0c8..7c83e43 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -19,6 +19,7 @@
 const val ANDROIDX_TEST_CORE = "androidx.test:core:1.1.0"
 const val ANDROIDX_TEST_EXT_JUNIT = "androidx.test.ext:junit:1.1.0"
 const val ANDROIDX_TEST_EXT_KTX = "androidx.test.ext:junit-ktx:1.1.0"
+const val ANDROIDX_TEST_MONITOR = "androidx.test:monitor:1.1.1"
 const val ANDROIDX_TEST_RULES = "androidx.test:rules:1.1.0"
 const val ANDROIDX_TEST_RUNNER = "androidx.test:runner:1.1.1"
 const val ANDROIDX_TEST_UIAUTOMATOR = "androidx.test.uiautomator:uiautomator:2.2.0"
@@ -68,6 +69,8 @@
     "org.jetbrains.kotlinx:kotlinx-coroutines-guava:$KOTLIN_COROUTINES_VERSION"
 const val KOTLIN_COROUTINES_TEST =
     "org.jetbrains.kotlinx:kotlinx-coroutines-test:$KOTLIN_COROUTINES_VERSION"
+const val KOTLIN_COROUTINES_PREVIEW =
+    "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0-RC"
 
 const val LEAKCANARY_INSTRUMENTATION =
     "com.squareup.leakcanary:leakcanary-android-instrumentation:1.6.2"
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt
index 3939416..7c8a6528 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt
@@ -25,11 +25,14 @@
 import org.gradle.api.tasks.TaskAction
 import java.io.File
 
-// Validate an API signature text file against a set of source files.
+// Validate that the API described in one signature txt file is compatible with the API in another
 abstract class CheckApiCompatibilityTask : MetalavaTask() {
     // Text file from which the API signatures will be obtained.
     @get:Input
     abstract val referenceApi: Property<ApiLocation>
+
+    @get:Input
+    abstract val api: Property<ApiLocation>
     // Text file listing violations that should be ignored
     @get:Input
     abstract val baselines: Property<ApiViolationBaselines>
@@ -56,25 +59,25 @@
     fun exec() {
         check(bootClasspath.isNotEmpty()) { "Android boot classpath not set." }
 
-        checkApiFile(referenceApi.get().publicApiFile, baselines.get().publicApiFile, false)
+        checkApiFile(api.get().publicApiFile, referenceApi.get().publicApiFile,
+            baselines.get().publicApiFile, false)
         if (checkRestrictedAPIs) {
-            checkApiFile(referenceApi.get().restrictedApiFile,
+            checkApiFile(api.get().restrictedApiFile, referenceApi.get().restrictedApiFile,
                 baselines.get().restrictedApiFile,
                 true)
         }
     }
 
-    // Confirms that the public API of this library (or the restricted API, if <checkRestrictedAPIs> is set
-    // is compatible with <apiFile> except for any baselines listed in <baselineFile>
-    fun checkApiFile(apiFile: File, baselineFile: File, checkRestrictedAPIs: Boolean) {
+    // Confirms that <api> is compatible with <oldApi> except for any baselines listed in <baselineFile>
+    fun checkApiFile(api: File, oldApi: File, baselineFile: File, checkRestrictedAPIs: Boolean) {
         var args = listOf("--classpath",
                 (bootClasspath + dependencyClasspath.files).joinToString(File.pathSeparator),
 
-                "--source-path",
-                sourcePaths.filter { it.exists() }.joinToString(File.pathSeparator),
+                "--source-files",
+                api.toString(),
 
                 "--check-compatibility:api:released",
-                apiFile.toString(),
+                oldApi.toString(),
 
                 "--warnings-as-errors",
                 "--format=v3"
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
index f5c5aed..ab64ddd 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
@@ -38,7 +38,9 @@
         it.classpath = checkNotNull(configuration) { "Configuration not set." }
         it.main = "com.android.tools.metalava.Driver"
         it.args = listOf(
-            "--no-banner"
+            "--no-banner",
+            "--hide",
+            "HiddenSuperclass" // We allow having a hidden parent class
         ) + args
     }
 }
@@ -50,10 +52,10 @@
         // The list of checks that are hidden as they are not useful in androidx
         "Enum", // Enums are allowed to be use in androidx
         "CallbackInterface", // With target Java 8, we have default methods
-        "HiddenSuperclass", // We allow having a hidden parent class
         "ProtectedMember", // We allow using protected members in androidx
         "ManagerLookup", // Managers in androidx are not the same as platfrom services
         "ManagerConstructor",
+        "RethrowRemoteException", // This check is for calls into system_server
 
         // List of checks that have bugs, but should be enabled once fixed.
         "GetterSetterNames", // b/135498039
@@ -181,6 +183,12 @@
         }
     }
 
+    // Never track @Experimental APIs.
+    args += listOf(
+        "--hide-annotation", "androidx.annotation.experimental.Experimental",
+        "--hide-meta-annotation", "androidx.annotation.experimental.Experimental"
+    )
+
     val metalavaConfiguration = getMetalavaConfiguration()
     runMetalavaWithArgs(metalavaConfiguration, args)
 
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
index f435065..51d97a4 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
@@ -141,7 +141,10 @@
                 task.baselines.set(baselines)
                 task.dependsOn(metalavaConfiguration)
                 task.checkRestrictedAPIs = extension.trackRestrictedAPIs
-                applyInputs(javaCompileInputs, task)
+                task.api.set(builtApiLocation)
+                task.dependencyClasspath = javaCompileInputs.dependencyClasspath
+                task.bootClasspath = javaCompileInputs.bootClasspath
+                task.dependsOn(generateApi)
             }
 
             project.tasks.register("ignoreApiChanges", IgnoreApiChangesTask::class.java) { task ->
@@ -149,7 +152,10 @@
                 task.referenceApi.set(checkApiRelease!!.flatMap { it.referenceApi })
                 task.baselines.set(checkApiRelease!!.flatMap { it.baselines })
                 task.processRestrictedApis = extension.trackRestrictedAPIs
-                applyInputs(javaCompileInputs, task)
+                task.api.set(builtApiLocation)
+                task.dependencyClasspath = javaCompileInputs.dependencyClasspath
+                task.bootClasspath = javaCompileInputs.bootClasspath
+                task.dependsOn(generateApi)
             }
         }
 
@@ -183,6 +189,14 @@
             task.outputApiLocations.set(checkApi.flatMap { it.checkedInApis })
             task.updateRestrictedAPIs = extension.trackRestrictedAPIs
             task.dependsOn(generateApi)
+            if (checkApiRelease != null) {
+                // If a developer (accidentally) makes a non-backwards compatible change to an
+                // api, the developer will want to be informed of it as soon as possible.
+                // So, whenever a developer updates an api, if backwards compatibility checks are
+                // enabled in the library, then we want to check that the changes are backwards
+                // compatible
+                task.dependsOn(checkApiRelease)
+            }
         }
 
         project.tasks.register("regenerateOldApis", RegenerateOldApisTask::class.java) { task ->
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt
index 559f3fb..733d8fd 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt
@@ -72,7 +72,6 @@
             runnerProject.logger.info("Skipping illegal version $version from $mavenId")
             return
         }
-        project.logger.lifecycle("Regenerating $mavenId")
         val inputs: JavaCompileInputs?
         try {
             inputs = getFiles(runnerProject, mavenId)
@@ -84,6 +83,7 @@
         val outputApiLocation = project.getApiLocation(version)
         val tempDir = File(project.buildDir, "api")
         if (outputApiLocation.publicApiFile.exists()) {
+            project.logger.lifecycle("Regenerating $mavenId")
             val generateRestrictedAPIs = outputApiLocation.restrictedApiFile.exists()
             project.generateApi(
                 inputs, outputApiLocation, tempDir, ApiLintMode.Skip, generateRestrictedAPIs)
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
index 5cab0d5..9d20a50 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
@@ -45,10 +45,13 @@
         val args = getCommonBaselineUpdateArgs(
             bootClasspath,
             dependencyClasspath,
-            sourcePaths,
             baselines.get().apiLintFile
         )
-        args += API_LINT_ARGS
+        args.addAll(API_LINT_ARGS + listOf<String>(
+            "--source-path",
+            sourcePaths.filter { it.exists() }.joinToString(File.pathSeparator)
+        ))
+
         runWithArgs(args)
     }
 }
@@ -63,6 +66,9 @@
     @get:Input
     abstract val referenceApi: Property<ApiLocation>
 
+    @get:Input
+    abstract val api: Property<ApiLocation>
+
     // The baseline files (api/*.*.*.ignore) to update
     @get:Input
     abstract val baselines: Property<ApiViolationBaselines>
@@ -93,12 +99,14 @@
         check(bootClasspath.isNotEmpty()) { "Android boot classpath not set." }
 
         updateBaseline(
+            api.get().publicApiFile,
             referenceApi.get().publicApiFile,
             baselines.get().publicApiFile,
             false
         )
         if (processRestrictedApis && referenceApi.get().restrictedApiFile.exists()) {
             updateBaseline(
+                api.get().restrictedApiFile,
                 referenceApi.get().restrictedApiFile,
                 baselines.get().restrictedApiFile,
                 true
@@ -109,19 +117,21 @@
     // Updates the contents of baselineFile to specify an exception for every API present in apiFile but not
     // present in the current source path
     private fun updateBaseline(
-        apiFile: File,
+        api: File,
+        prevApi: File,
         baselineFile: File,
         processRestrictedApis: Boolean
     ) {
         val args = getCommonBaselineUpdateArgs(
             bootClasspath,
             dependencyClasspath,
-            sourcePaths,
             baselineFile
         )
         args += listOf(
             "--check-compatibility:api:released",
-            apiFile.toString()
+            prevApi.toString(),
+            "--source-files",
+            api.toString()
         )
         if (processRestrictedApis) {
             args += listOf(
@@ -137,7 +147,6 @@
 private fun getCommonBaselineUpdateArgs(
     bootClasspath: Collection<File>,
     dependencyClasspath: FileCollection,
-    sourcePaths: Collection<File>,
     baselineFile: File
 ): MutableList<String> {
     // Create the baseline file if it does exist, as Metalava cannot handle non-existent files.
@@ -146,9 +155,6 @@
         "--classpath",
         (bootClasspath + dependencyClasspath.files).joinToString(File.pathSeparator),
 
-        "--source-path",
-        sourcePaths.filter { it.exists() }.joinToString(File.pathSeparator),
-
         "--update-baseline",
         baselineFile.toString(),
         "--baseline", baselineFile.toString(),
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index 2a15464..b13de44 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -27,7 +27,7 @@
 dependencies {
     api(project(":camera:camera-core"))
 
-    implementation("androidx.core:core:1.1.0-rc01")
+    implementation("androidx.core:core:1.1.0")
     implementation("androidx.annotation:annotation:1.0.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0-alpha03")
     implementation(GUAVA_LISTENABLE_FUTURE)
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 2fe9758..f11a503 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
@@ -34,6 +34,7 @@
 import androidx.camera.core.ImageReaderProxys;
 import androidx.camera.core.ImmediateSurface;
 import androidx.camera.core.SessionConfig;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.testing.CameraUtil;
 import androidx.camera.testing.fakes.FakeUseCase;
 import androidx.camera.testing.fakes.FakeUseCaseConfig;
@@ -60,6 +61,7 @@
     private BaseCamera mCamera;
     private HandlerThread mHandlerThread;
     private Handler mHandler;
+    private List<ImageReaderProxy> mReaders = new ArrayList<>();
 
     private static ImageReaderProxy.OnImageAvailableListener createSemaphoreReleasingListener(
             final Semaphore semaphore) {
@@ -76,7 +78,7 @@
     }
 
     @Before
-    public void setUp()  {
+    public void setUp() {
         assumeTrue(CameraUtil.deviceHasCamera());
         Context context = ApplicationProvider.getApplicationContext();
         AppConfig appConfig = Camera2AppConfig.create(context);
@@ -86,11 +88,15 @@
         mHandlerThread = new HandlerThread("Background");
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
+        mReaders = new ArrayList<>();
     }
 
     @After
     public void tearDown() {
-        if (mCamera !=  null && mHandlerThread != null) {
+        for (ImageReaderProxy reader : mReaders) {
+            reader.close();
+        }
+        if (mCamera != null && mHandlerThread != null) {
             mCamera.release();
             mHandlerThread.quitSafely();
         }
@@ -99,21 +105,21 @@
     @MediumTest
     @Test
     public void sharedReadersGetFramesFromCamera() throws InterruptedException {
-        List<ImageReaderProxy> readers = new ArrayList<>();
         List<Semaphore> semaphores = new ArrayList<>();
         for (int i = 0; i < 2; ++i) {
             ImageReaderProxy reader =
                     ImageReaderProxys.createSharedReader(
-                            CAMERA_ID, 640, 480, ImageFormat.YUV_420_888, 2, mHandler);
+                            CAMERA_ID, 640, 480, ImageFormat.YUV_420_888, 2,
+                            CameraXExecutors.newHandlerExecutor(mHandler));
             Semaphore semaphore = new Semaphore(/*permits=*/ 0);
             reader.setOnImageAvailableListener(
                     createSemaphoreReleasingListener(semaphore), mHandler);
-            readers.add(reader);
+            mReaders.add(reader);
             semaphores.add(semaphore);
         }
 
         FakeUseCaseConfig config = new FakeUseCaseConfig.Builder().setTargetName("UseCase").build();
-        UseCase useCase = new UseCase(config, readers);
+        UseCase useCase = new UseCase(config, mReaders);
         CameraUtil.openCameraWithUseCase(CAMERA_ID, mCamera, useCase);
 
         // Wait for a few frames to be observed.
@@ -125,21 +131,20 @@
     @MediumTest
     @Test
     public void isolatedReadersGetFramesFromCamera() throws InterruptedException {
-        List<ImageReaderProxy> readers = new ArrayList<>();
         List<Semaphore> semaphores = new ArrayList<>();
         for (int i = 0; i < 2; ++i) {
             ImageReaderProxy reader =
                     ImageReaderProxys.createIsolatedReader(
-                            640, 480, ImageFormat.YUV_420_888, 2, mHandler);
+                            640, 480, ImageFormat.YUV_420_888, 2);
             Semaphore semaphore = new Semaphore(/*permits=*/ 0);
             reader.setOnImageAvailableListener(
                     createSemaphoreReleasingListener(semaphore), mHandler);
-            readers.add(reader);
+            mReaders.add(reader);
             semaphores.add(semaphore);
         }
 
         FakeUseCaseConfig config = new FakeUseCaseConfig.Builder().setTargetName("UseCase").build();
-        UseCase useCase = new UseCase(config, readers);
+        UseCase useCase = new UseCase(config, mReaders);
         CameraUtil.openCameraWithUseCase(CAMERA_ID, mCamera, useCase);
 
         // Wait for a few frames to be observed.
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/CaptureSessionTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/CaptureSessionTest.java
index cd153b5..507177c 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/CaptureSessionTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/CaptureSessionTest.java
@@ -205,7 +205,8 @@
         CaptureSession captureSession = new CaptureSession(mTestParameters0.mHandler);
         captureSession.setSessionConfig(mTestParameters0.mSessionConfig);
 
-        ListenableFuture<Void> releaseFuture = captureSession.release();
+        ListenableFuture<Void> releaseFuture = captureSession.release(
+                /*abortInFlightCaptures=*/false);
 
         // Wait for release
         releaseFuture.get();
@@ -227,7 +228,8 @@
         assertThat(captureSession.getState()).isEqualTo(State.CLOSED);
 
         // Release the session to clean up for next test
-        ListenableFuture<Void> releaseFuture = captureSession.release();
+        ListenableFuture<Void> releaseFuture = captureSession.release(
+                /*abortInFlightCaptures=*/false);
 
         // Wait for release to finish
         releaseFuture.get();
@@ -239,7 +241,8 @@
         CaptureSession captureSession = new CaptureSession(mTestParameters0.mHandler);
         captureSession.setSessionConfig(mTestParameters0.mSessionConfig);
         captureSession.open(mTestParameters0.mSessionConfig, mCameraDevice);
-        ListenableFuture<Void> releaseFuture = captureSession.release();
+        ListenableFuture<Void> releaseFuture = captureSession.release(
+                /*abortInFlightCaptures=*/false);
 
         // Wait for release
         releaseFuture.get();
@@ -418,7 +421,8 @@
         };
         mTestParameters0.mDeferrableSurface.setOnSurfaceDetachedListener(executor, listener);
 
-        ListenableFuture<Void> releaseFuture = captureSession.release();
+        ListenableFuture<Void> releaseFuture = captureSession.release(
+                /*abortInFlightCaptures=*/false);
 
         // Wait for release
         releaseFuture.get();
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera.java
index 9a61ef0..8896381 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera.java
@@ -264,7 +264,7 @@
 
     void closeCameraResource() {
         mCaptureSession.close();
-        mCaptureSession.release();
+        mCaptureSession.release(/*abortInFlightCaptures=*/true);
         mCameraDevice.close();
         notifyCameraDeviceCloseToCaptureSessions();
         mCameraDevice = null;
@@ -695,7 +695,7 @@
         SessionConfig previousSessionConfig = mCaptureSession.getSessionConfig();
 
         mCaptureSession.close();
-        mCaptureSession.release();
+        mCaptureSession.release(/*abortInFlightCaptures=*/false);
 
         // Saves the closed CaptureSessions if device is not closed yet.
         // We need to notify camera device closed event to these CaptureSessions.
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManager.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManager.java
index 7b1b45b..71303cd 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManager.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManager.java
@@ -30,7 +30,6 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.camera.core.CameraDeviceConfig;
 import androidx.camera.core.CameraDeviceSurfaceManager;
-import androidx.camera.core.CameraInfoUnavailableException;
 import androidx.camera.core.CameraX;
 import androidx.camera.core.SurfaceConfig;
 import androidx.camera.core.UseCase;
@@ -50,6 +49,7 @@
  * of surface configuration type and size pairs can be supported for different hardware level camera
  * devices. This structure is used to store the guaranteed supported stream capabilities related
  * info.
+ *
  * @hide
  */
 @RestrictTo(Scope.LIBRARY)
@@ -302,11 +302,16 @@
     }
 
     private String getCameraIdFromConfig(UseCaseConfig<?> useCaseConfig) {
+        CameraDeviceConfig config = (CameraDeviceConfig) useCaseConfig;
         String cameraId;
         try {
-            cameraId =
-                    CameraX.getCameraWithCameraDeviceConfig((CameraDeviceConfig) useCaseConfig);
-        } catch (CameraInfoUnavailableException e) {
+            CameraX.LensFacing lensFacing = config.getLensFacing(null);
+            // Adds default lensFacing if the user doesn't specify the lens facing.
+            if (lensFacing == null) {
+                lensFacing = CameraX.getDefaultLensFacing();
+            }
+            cameraId = CameraX.getCameraWithLensFacing(lensFacing);
+        } catch (Exception e) {
             throw new IllegalArgumentException(
                     "Unable to get camera ID for use case " + useCaseConfig.getTargetName(), e);
         }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/CaptureSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/CaptureSession.java
index 31ed13e..2f69596 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/CaptureSession.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/CaptureSession.java
@@ -345,7 +345,7 @@
      * <p>Once a session is released it can no longer be opened again. After the session is released
      * all method calls on it do nothing.
      */
-    ListenableFuture<Void> release() {
+    ListenableFuture<Void> release(boolean abortInFlightCaptures) {
         synchronized (mStateLock) {
             switch (mState) {
                 case UNINITIALIZED:
@@ -354,6 +354,15 @@
                 case OPENED:
                 case CLOSED:
                     if (mCameraCaptureSession != null) {
+                        if (abortInFlightCaptures) {
+                            try {
+                                mCameraCaptureSession.abortCaptures();
+                            } catch (CameraAccessException e) {
+                                // We couldn't abort the captures, but we should continue on to
+                                // release the session.
+                                Log.e(TAG, "Unable to abort captures.", e);
+                            }
+                        }
                         mCameraCaptureSession.close();
                     }
                     // Fall through
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageAnalysisConfigProvider.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageAnalysisConfigProvider.java
index 7ec4ccd..3719aab 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageAnalysisConfigProvider.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageAnalysisConfigProvider.java
@@ -33,9 +33,6 @@
 import androidx.camera.core.ImageAnalysisConfig;
 import androidx.camera.core.SessionConfig;
 
-import java.util.Arrays;
-import java.util.List;
-
 /**
  * Provides defaults for {@link ImageAnalysisConfig} in the Camera2 implementation.
  * @hide
@@ -74,24 +71,13 @@
         builder.setDefaultCaptureConfig(captureBuilder.build());
         builder.setCaptureOptionUnpacker(Camera2CaptureOptionUnpacker.INSTANCE);
 
-        List<LensFacing> lensFacingList;
-
-        // Add default lensFacing if we can
-        if (lensFacing == LensFacing.FRONT) {
-            lensFacingList = Arrays.asList(LensFacing.FRONT, LensFacing.BACK);
-        } else {
-            lensFacingList = Arrays.asList(LensFacing.BACK, LensFacing.FRONT);
-        }
-
         try {
-            String defaultId = null;
-
-            for (LensFacing lensFacingCandidate : lensFacingList) {
-                defaultId = mCameraFactory.cameraIdForLensFacing(lensFacingCandidate);
-                if (defaultId != null) {
-                    builder.setLensFacing(lensFacingCandidate);
-                    break;
-                }
+            // Add default lensFacing if we can
+            LensFacing checkedLensFacing =
+                    (lensFacing != null) ? lensFacing : CameraX.getDefaultLensFacing();
+            String defaultId = mCameraFactory.cameraIdForLensFacing(checkedLensFacing);
+            if (defaultId != null) {
+                builder.setLensFacing(checkedLensFacing);
             }
 
             int targetRotation = mWindowManager.getDefaultDisplay().getRotation();
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageCaptureConfigProvider.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageCaptureConfigProvider.java
index 158fa34..b8b2951 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageCaptureConfigProvider.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/ImageCaptureConfigProvider.java
@@ -33,9 +33,6 @@
 import androidx.camera.core.ImageCaptureConfig;
 import androidx.camera.core.SessionConfig;
 
-import java.util.Arrays;
-import java.util.List;
-
 /**
  * Provides defaults for {@link ImageCaptureConfig} in the Camera2 implementation.
  * @hide
@@ -74,24 +71,13 @@
         builder.setDefaultCaptureConfig(captureConfig.build());
         builder.setCaptureOptionUnpacker(ImageCaptureOptionUnpacker.INSTANCE);
 
-        List<LensFacing> lensFacingList;
-
-        // Add default lensFacing if we can
-        if (lensFacing == LensFacing.FRONT) {
-            lensFacingList = Arrays.asList(LensFacing.FRONT, LensFacing.BACK);
-        } else {
-            lensFacingList = Arrays.asList(LensFacing.BACK, LensFacing.FRONT);
-        }
-
         try {
-            String defaultId = null;
-
-            for (LensFacing lensFacingCandidate : lensFacingList) {
-                defaultId = mCameraFactory.cameraIdForLensFacing(lensFacingCandidate);
-                if (defaultId != null) {
-                    builder.setLensFacing(lensFacingCandidate);
-                    break;
-                }
+            // Add default lensFacing if we can
+            LensFacing checkedLensFacing =
+                    (lensFacing != null) ? lensFacing : CameraX.getDefaultLensFacing();
+            String defaultId = mCameraFactory.cameraIdForLensFacing(checkedLensFacing);
+            if (defaultId != null) {
+                builder.setLensFacing(checkedLensFacing);
             }
 
             int targetRotation = mWindowManager.getDefaultDisplay().getRotation();
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/PreviewConfigProvider.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/PreviewConfigProvider.java
index 290cd6f..e58ca95 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/PreviewConfigProvider.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/PreviewConfigProvider.java
@@ -33,9 +33,6 @@
 import androidx.camera.core.PreviewConfig;
 import androidx.camera.core.SessionConfig;
 
-import java.util.Arrays;
-import java.util.List;
-
 /**
  * Provides defaults for {@link PreviewConfig} in the Camera2 implementation.
  * @hide
@@ -72,24 +69,13 @@
         builder.setDefaultCaptureConfig(captureBuilder.build());
         builder.setCaptureOptionUnpacker(Camera2CaptureOptionUnpacker.INSTANCE);
 
-        List<LensFacing> lensFacingList;
-
-        // Add default lensFacing if we can
-        if (lensFacing == LensFacing.FRONT) {
-            lensFacingList = Arrays.asList(LensFacing.FRONT, LensFacing.BACK);
-        } else {
-            lensFacingList = Arrays.asList(LensFacing.BACK, LensFacing.FRONT);
-        }
-
         try {
-            String defaultId = null;
-
-            for (LensFacing lensFacingCandidate : lensFacingList) {
-                defaultId = mCameraFactory.cameraIdForLensFacing(lensFacingCandidate);
-                if (defaultId != null) {
-                    builder.setLensFacing(lensFacingCandidate);
-                    break;
-                }
+            // Add default lensFacing if we can
+            LensFacing checkedLensFacing =
+                    (lensFacing != null) ? lensFacing : CameraX.getDefaultLensFacing();
+            String defaultId = mCameraFactory.cameraIdForLensFacing(checkedLensFacing);
+            if (defaultId != null) {
+                builder.setLensFacing(checkedLensFacing);
             }
 
             int targetRotation = mWindowManager.getDefaultDisplay().getRotation();
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/VideoCaptureConfigProvider.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/VideoCaptureConfigProvider.java
index 1f91fad..807ef4a 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/VideoCaptureConfigProvider.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/VideoCaptureConfigProvider.java
@@ -33,9 +33,6 @@
 import androidx.camera.core.VideoCapture;
 import androidx.camera.core.VideoCaptureConfig;
 
-import java.util.Arrays;
-import java.util.List;
-
 /**
  * Provides defaults for {@link VideoCaptureConfig} in the Camera2 implementation.
  * @hide
@@ -74,24 +71,13 @@
         builder.setDefaultCaptureConfig(captureBuilder.build());
         builder.setCaptureOptionUnpacker(Camera2CaptureOptionUnpacker.INSTANCE);
 
-        List<LensFacing> lensFacingList;
-
-        // Add default lensFacing if we can
-        if (lensFacing == LensFacing.FRONT) {
-            lensFacingList = Arrays.asList(LensFacing.FRONT, LensFacing.BACK);
-        } else {
-            lensFacingList = Arrays.asList(LensFacing.BACK, LensFacing.FRONT);
-        }
-
         try {
-            String defaultId = null;
-
-            for (LensFacing lensFacingCandidate : lensFacingList) {
-                defaultId = mCameraFactory.cameraIdForLensFacing(lensFacingCandidate);
-                if (defaultId != null) {
-                    builder.setLensFacing(lensFacingCandidate);
-                    break;
-                }
+            // Add default lensFacing if we can
+            LensFacing checkedLensFacing =
+                    (lensFacing != null) ? lensFacing : CameraX.getDefaultLensFacing();
+            String defaultId = mCameraFactory.cameraIdForLensFacing(checkedLensFacing);
+            if (defaultId != null) {
+                builder.setLensFacing(checkedLensFacing);
             }
 
             int targetRotation = mWindowManager.getDefaultDisplay().getRotation();
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2CaptureRequestBuilderTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2CaptureRequestBuilderTest.java
index 84e213d..aa325cd 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2CaptureRequestBuilderTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2CaptureRequestBuilderTest.java
@@ -21,6 +21,7 @@
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
+import android.os.Build;
 import android.view.Surface;
 
 import androidx.camera.core.CaptureConfig;
@@ -30,6 +31,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
 import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.util.HashMap;
@@ -37,7 +39,9 @@
 @SmallTest
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 public class Camera2CaptureRequestBuilderTest {
+
     @Test
     public void buildCaptureRequestWithNullCameraDevice() throws CameraAccessException {
         CameraDevice cameraDevice = null;
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2LensFacingCameraIdFilterTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2LensFacingCameraIdFilterTest.java
index eb2398f..7054b98 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2LensFacingCameraIdFilterTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2LensFacingCameraIdFilterTest.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraManager;
+import android.os.Build;
 
 import androidx.camera.core.CameraX;
 import androidx.test.core.app.ApplicationProvider;
@@ -30,6 +31,7 @@
 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.ShadowCameraCharacteristics;
@@ -41,6 +43,7 @@
 @SmallTest
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 public class Camera2LensFacingCameraIdFilterTest {
     private static final String CAMERA0_ID = "0";
     private static final int CAMERA0_LENS_FACING_INT = CameraCharacteristics.LENS_FACING_BACK;
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java
index d93503b..2e10371 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java
@@ -516,6 +516,34 @@
     }
 
     @Test
+    public void checkDefaultLensFacingForMixedUseCase() {
+        setupCamera(/* supportsRaw= */ false);
+
+        Preview preview = new Preview(new PreviewConfig.Builder().build());
+        ImageCapture imageCapture = new ImageCapture(new ImageCaptureConfig.Builder().build());
+        ImageAnalysis imageAnalysis = new ImageAnalysis(new ImageAnalysisConfig.Builder().build());
+        VideoCapture videoCapture = new VideoCapture(new VideoCaptureConfig.Builder().build());
+
+        PreviewConfig previewConfig = (PreviewConfig) preview.getUseCaseConfig();
+        ImageCaptureConfig imageCaptureConfig =
+                (ImageCaptureConfig) imageCapture.getUseCaseConfig();
+        ImageAnalysisConfig imageAnalysisConfig =
+                (ImageAnalysisConfig) imageAnalysis.getUseCaseConfig();
+        VideoCaptureConfig videoCaptureConfig =
+                (VideoCaptureConfig) videoCapture.getUseCaseConfig();
+
+        LensFacing previewLensFacing = previewConfig.getLensFacing(null);
+        LensFacing imageCaptureLensFacing = imageCaptureConfig.getLensFacing(null);
+        LensFacing imageAnalysisLensFacing = imageAnalysisConfig.getLensFacing(null);
+        LensFacing videoCaptureLensFacing = videoCaptureConfig.getLensFacing(null);
+
+        assertThat(previewLensFacing).isNotNull();
+        assertThat(imageCaptureLensFacing).isNotNull();
+        assertThat(imageAnalysisLensFacing).isNotNull();
+        assertThat(videoCaptureLensFacing).isNotNull();
+    }
+
+    @Test
     public void suggestedResolutionsForMixedUseCaseNotSupportedInLegacyDevice() {
         setupCamera(/* supportsRaw= */ false);
         SupportedSurfaceCombination supportedSurfaceCombination =
diff --git a/camera/camera-core/build.gradle b/camera/camera-core/build.gradle
index d792fc0..b111e0d 100644
--- a/camera/camera-core/build.gradle
+++ b/camera/camera-core/build.gradle
@@ -29,7 +29,7 @@
     api("androidx.lifecycle:lifecycle-common:2.0.0")
     implementation("androidx.exifinterface:exifinterface:1.0.0")
     implementation("androidx.annotation:annotation:1.0.0")
-    implementation("androidx.core:core:1.1.0-rc01")
+    implementation("androidx.core:core:1.1.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0-alpha03")
     implementation(ARCH_LIFECYCLE_LIVEDATA)
 
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/AndroidImageReaderProxyTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/AndroidImageReaderProxyTest.java
index b5efe40..fec0614 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/AndroidImageReaderProxyTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/AndroidImageReaderProxyTest.java
@@ -29,6 +29,7 @@
 import android.os.Handler;
 import android.view.Surface;
 
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 
@@ -112,7 +113,8 @@
         ImageReaderProxy.OnImageAvailableListener listener =
                 mock(ImageReaderProxy.OnImageAvailableListener.class);
 
-        mImageReaderProxy.setOnImageAvailableListener(listener, /*handler=*/ null);
+        mImageReaderProxy.setOnImageAvailableListener(listener,
+                CameraXExecutors.directExecutor());
 
         ArgumentCaptor<ImageReader.OnImageAvailableListener> transformedListenerCaptor =
                 ArgumentCaptor.forClass(ImageReader.OnImageAvailableListener.class);
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/ForwardingImageReaderListenerTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/ForwardingImageReaderListenerTest.java
index cec4086..223548c 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/ForwardingImageReaderListenerTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/ForwardingImageReaderListenerTest.java
@@ -26,8 +26,6 @@
 import static org.mockito.Mockito.when;
 
 import android.graphics.ImageFormat;
-import android.media.Image;
-import android.media.ImageReader;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.view.Surface;
@@ -52,15 +50,15 @@
     private static final int IMAGE_FORMAT = ImageFormat.YUV_420_888;
     private static final int MAX_IMAGES = 10;
 
-    private final ImageReader mImageReader = mock(ImageReader.class);
+    private final ImageReaderProxy mImageReader = mock(ImageReaderProxy.class);
     private final Surface mSurface = mock(Surface.class);
     private HandlerThread mHandlerThread;
     private Handler mHandler;
     private List<QueuedImageReaderProxy> mImageReaderProxies;
     private ForwardingImageReaderListener mForwardingListener;
 
-    private static Image createMockImage() {
-        Image image = mock(Image.class);
+    private static ImageProxy createMockImage() {
+        ImageProxy image = mock(ImageProxy.class);
         when(image.getWidth()).thenReturn(IMAGE_WIDTH);
         when(image.getHeight()).thenReturn(IMAGE_HEIGHT);
         when(image.getFormat()).thenReturn(IMAGE_FORMAT);
@@ -107,7 +105,7 @@
 
     @Test
     public void newImageIsForwardedToAllListeners() {
-        Image baseImage = createMockImage();
+        ImageProxy baseImage = createMockImage();
         when(mImageReader.acquireNextImage()).thenReturn(baseImage);
         List<ImageReaderProxy.OnImageAvailableListener> listeners = new ArrayList<>();
         for (ImageReaderProxy imageReaderProxy : mImageReaderProxies) {
@@ -132,7 +130,7 @@
     public void baseImageIsClosed_allQueuesAreCleared_whenAllForwardedCopiesAreClosed()
             throws InterruptedException {
         Semaphore onCloseSemaphore = new Semaphore(/*permits=*/ 0);
-        Image baseImage = createMockImage();
+        ImageProxy baseImage = createMockImage();
         when(mImageReader.acquireNextImage()).thenReturn(baseImage);
         for (ImageReaderProxy imageReaderProxy : mImageReaderProxies) {
             // Close the image for every listener.
@@ -158,7 +156,7 @@
     public void baseImageIsNotClosed_someQueuesAreCleared_whenNotAllForwardedCopiesAreClosed()
             throws InterruptedException {
         Semaphore onCloseSemaphore = new Semaphore(/*permits=*/ 0);
-        Image baseImage = createMockImage();
+        ImageProxy baseImage = createMockImage();
         when(mImageReader.acquireNextImage()).thenReturn(baseImage);
         // Don't close the image for the first listener.
         mImageReaderProxies.get(0).setOnImageAvailableListener(createMockListener(), mHandler);
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/MetadataImageReaderTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/MetadataImageReaderTest.java
index 99b82f9..ecd5238 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/MetadataImageReaderTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/MetadataImageReaderTest.java
@@ -20,6 +20,7 @@
 
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Looper;
 
 import androidx.camera.testing.HandlerUtil;
 import androidx.camera.testing.fakes.FakeCameraCaptureResult;
@@ -49,6 +50,8 @@
     private final Semaphore mSemaphore = new Semaphore(0);
     private HandlerThread mBackgroundThread;
     private Handler mBackgroundHandler;
+
+    private Handler mMainHandler;
     private MetadataImageReader mMetadataImageReader;
 
     @Before
@@ -57,6 +60,8 @@
         mBackgroundThread.start();
         mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
 
+        mMainHandler = new Handler(Looper.getMainLooper());
+
         createMetadataImageReaderWithCapacity(8);
         mCameraCaptureResult0.setTimestamp(TIMESTAMP_0);
         mCameraCaptureResult1.setTimestamp(TIMESTAMP_1);
@@ -264,7 +269,7 @@
 
     private void createMetadataImageReaderWithCapacity(int maxImages) {
         mImageReader = new FakeImageReaderProxy(maxImages);
-        mMetadataImageReader = new MetadataImageReader(mImageReader, null);
+        mMetadataImageReader = new MetadataImageReader(mImageReader, mMainHandler);
     }
 
     private void triggerImageAvailable(long timestamp) throws InterruptedException {
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/QueuedImageReaderProxyTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/QueuedImageReaderProxyTest.java
index d7028b6..0310dd2 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/QueuedImageReaderProxyTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/QueuedImageReaderProxyTest.java
@@ -39,6 +39,8 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 import java.util.concurrent.Semaphore;
 
 @LargeTest
@@ -52,6 +54,7 @@
     private final Surface mSurface = mock(Surface.class);
     private HandlerThread mHandlerThread;
     private Handler mHandler;
+    private Executor mExecutor;
     private QueuedImageReaderProxy mImageReaderProxy;
 
     private static ImageProxy createMockImageProxy() {
@@ -84,6 +87,7 @@
         mHandlerThread = new HandlerThread("background");
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
+        mExecutor = Executors.newSingleThreadExecutor();
         mImageReaderProxy =
                 new QueuedImageReaderProxy(
                         IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_FORMAT, MAX_IMAGES, mSurface);
@@ -168,11 +172,23 @@
     }
 
     @Test
-    public void enqueueImage_invokesListenerCallback() {
+    public void enqueueImage_invokesListenerCallbackOnHandler() {
         ImageReaderProxy.OnImageAvailableListener listener =
                 mock(ImageReaderProxy.OnImageAvailableListener.class);
         mImageReaderProxy.setOnImageAvailableListener(listener, mHandler);
+        enqueueImage_invokesListenerCallback(listener);
+    }
 
+    @Test
+    public void enqueueImage_invokesListenerCallbackOnExecutor() {
+        ImageReaderProxy.OnImageAvailableListener listener =
+                mock(ImageReaderProxy.OnImageAvailableListener.class);
+        mImageReaderProxy.setOnImageAvailableListener(listener, mExecutor);
+        enqueueImage_invokesListenerCallback(listener);
+    }
+
+    private void enqueueImage_invokesListenerCallback(
+            ImageReaderProxy.OnImageAvailableListener listener) {
         mImageReaderProxy.enqueueImage(createForwardingImageProxy());
         mImageReaderProxy.enqueueImage(createForwardingImageProxy());
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/AndroidImageReaderProxy.java b/camera/camera-core/src/main/java/androidx/camera/core/AndroidImageReaderProxy.java
index 2a304e0..bef28dd 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/AndroidImageReaderProxy.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/AndroidImageReaderProxy.java
@@ -22,7 +22,12 @@
 import android.view.Surface;
 
 import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.camera.core.impl.utils.MainThreadAsyncHandler;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
+
+import java.util.concurrent.Executor;
 
 /**
  * An {@link ImageReaderProxy} which wraps around an {@link ImageReader}.
@@ -96,15 +101,35 @@
 
     @Override
     public synchronized void setOnImageAvailableListener(
-            @Nullable final ImageReaderProxy.OnImageAvailableListener listener,
+            @NonNull final ImageReaderProxy.OnImageAvailableListener listener,
             @Nullable Handler handler) {
+        // Unlike ImageReader.setOnImageAvailableListener(), if handler == null, the callback
+        // will not be triggered at all, instead of being triggered on main thread.
+        setOnImageAvailableListener(listener,
+                handler == null ? null : CameraXExecutors.newHandlerExecutor(handler));
+    }
+
+    @Override
+    public synchronized void setOnImageAvailableListener(
+            @NonNull OnImageAvailableListener listener,
+            @NonNull Executor executor) {
+        // ImageReader does not accept an executor. As a workaround, the callback is run on main
+        // handler then immediately posted to the executor.
         ImageReader.OnImageAvailableListener transformedListener =
                 new ImageReader.OnImageAvailableListener() {
                     @Override
                     public void onImageAvailable(ImageReader reader) {
-                        listener.onImageAvailable(AndroidImageReaderProxy.this);
+                        executor.execute(new Runnable() {
+                            @Override
+                            public void run() {
+                                listener.onImageAvailable(AndroidImageReaderProxy.this);
+                            }
+                        });
+
                     }
                 };
-        mImageReader.setOnImageAvailableListener(transformedListener, handler);
+        mImageReader.setOnImageAvailableListener(transformedListener,
+                MainThreadAsyncHandler.getInstance());
     }
+
 }
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 e5d2998a..0679266 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
@@ -29,6 +29,7 @@
 import androidx.lifecycle.LifecycleOwner;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
@@ -352,6 +353,30 @@
     }
 
     /**
+     * Gets the default lens facing or {@code null} if there is no available camera.
+     *
+     * @return The default lens facing or {@code null}.
+     * @throws CameraInfoUnavailableException if unable to access cameras, perhaps due to
+     *                                        insufficient permissions.
+     * @hide
+     */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    @Nullable
+    public static LensFacing getDefaultLensFacing()
+            throws CameraInfoUnavailableException {
+        LensFacing lensFacingCandidate = null;
+        List<LensFacing> lensFacingList = Arrays.asList(LensFacing.BACK, LensFacing.FRONT);
+        for (LensFacing lensFacing : lensFacingList) {
+            String cameraId = INSTANCE.getCameraFactory().cameraIdForLensFacing(lensFacing);
+            if (cameraId != null) {
+                lensFacingCandidate = lensFacing;
+                break;
+            }
+        }
+        return lensFacingCandidate;
+    }
+
+    /**
      * Returns the camera info for the camera with the given camera id.
      *
      * @param cameraId the internal id of the camera
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ForwardingImageProxy.java b/camera/camera-core/src/main/java/androidx/camera/core/ForwardingImageProxy.java
index 60ac352..76dd526 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ForwardingImageProxy.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ForwardingImageProxy.java
@@ -51,7 +51,7 @@
     }
 
     @Override
-    public synchronized void close() {
+    public void close() {
         mImage.close();
         notifyOnImageCloseListeners();
     }
@@ -116,8 +116,15 @@
     }
 
     /** Notifies the listeners that this image has been closed. */
-    protected synchronized void notifyOnImageCloseListeners() {
-        for (OnImageCloseListener listener : mOnImageCloseListeners) {
+    protected void notifyOnImageCloseListeners() {
+        Set<OnImageCloseListener> onImageCloseListeners;
+        synchronized (this) {
+            // Make a copy for thread safety. We want to synchronize the access for member variables
+            // but not the actual callbacks to avoid a deadlock between ForwardingImageProxy and
+            // QueuedImageReaderProxy. go/deadlock-in-sharedimagereaderproxy
+            onImageCloseListeners = new HashSet<>(mOnImageCloseListeners);
+        }
+        for (OnImageCloseListener listener : onImageCloseListeners) {
             listener.onImageClose(this);
         }
     }
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 3b92666..e94c9d60 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
@@ -16,19 +16,17 @@
 
 package androidx.camera.core;
 
-import android.media.Image;
-import android.media.ImageReader;
-
 import androidx.annotation.GuardedBy;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 /**
- * An {@link ImageReader.OnImageAvailableListener} which forks and forwards newly available images
- * to multiple {@link ImageReaderProxy} instances.
+ * An {@link ImageReaderProxy.OnImageAvailableListener} which forks and forwards newly available
+ * images to multiple {@link ImageReaderProxy} instances.
  */
-final class ForwardingImageReaderListener implements ImageReader.OnImageAvailableListener {
+final class ForwardingImageReaderListener implements ImageReaderProxy.OnImageAvailableListener {
     @GuardedBy("this")
     private final List<QueuedImageReaderProxy> mImageReaders;
 
@@ -39,26 +37,26 @@
      * @return new {@link ForwardingImageReaderListener} instance
      */
     ForwardingImageReaderListener(List<QueuedImageReaderProxy> imageReaders) {
-        mImageReaders = Collections.unmodifiableList(imageReaders);
+        // Make a copy of the incoming List to avoid ConcurrentAccessException.
+        mImageReaders = Collections.unmodifiableList(new ArrayList<>(imageReaders));
     }
 
     @Override
-    public synchronized void onImageAvailable(ImageReader imageReader) {
-        Image image = imageReader.acquireNextImage();
-        ImageProxy imageProxy = new AndroidImageProxy(image);
+    public synchronized void onImageAvailable(ImageReaderProxy imageReaderProxy) {
+        ImageProxy imageProxy = imageReaderProxy.acquireNextImage();
         ReferenceCountedImageProxy referenceCountedImageProxy =
                 new ReferenceCountedImageProxy(imageProxy);
-        for (QueuedImageReaderProxy imageReaderProxy : mImageReaders) {
-            synchronized (imageReaderProxy) {
-                if (!imageReaderProxy.isClosed()) {
+        for (QueuedImageReaderProxy queuedImageReaderProxy : mImageReaders) {
+            synchronized (queuedImageReaderProxy) {
+                if (!queuedImageReaderProxy.isClosed()) {
                     ImageProxy forkedImage = referenceCountedImageProxy.fork();
                     ForwardingImageProxy imageToEnqueue =
                             ImageProxyDownsampler.downsample(
                                     forkedImage,
-                                    imageReaderProxy.getWidth(),
-                                    imageReaderProxy.getHeight(),
+                                    queuedImageReaderProxy.getWidth(),
+                                    queuedImageReaderProxy.getHeight(),
                                     ImageProxyDownsampler.DownsamplingMethod.AVERAGING);
-                    imageReaderProxy.enqueueImage(imageToEnqueue);
+                    queuedImageReaderProxy.enqueueImage(imageToEnqueue);
                 }
             }
         }
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 96a1792..5733c02 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
@@ -33,6 +33,7 @@
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 
 import java.util.Map;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -45,7 +46,6 @@
  *
  * <p>After the analyzer function returns, the {@link ImageProxy} will be closed and the
  * corresponding {@link android.media.Image} is released back to the {@link ImageReader}.
- *
  */
 public final class ImageAnalysis extends UseCase {
     /**
@@ -58,7 +58,7 @@
     private static final String TAG = "ImageAnalysis";
     final AtomicReference<Analyzer> mSubscribedAnalyzer;
     final AtomicInteger mRelativeRotation = new AtomicInteger();
-    private final Handler mHandler;
+    final Handler mHandler;
     private final ImageAnalysisConfig.Builder mUseCaseConfigBuilder;
     @Nullable
     ImageReaderProxy mImageReader;
@@ -257,6 +257,9 @@
             mImageReader.close();
         }
 
+        Executor backgroundExecutor = config.getBackgroundExecutor(
+                CameraXExecutors.highPriorityExecutor());
+
         mImageReader =
                 ImageReaderProxys.createCompatibleReader(
                         cameraId,
@@ -264,7 +267,7 @@
                         resolution.getHeight(),
                         getImageFormat(),
                         config.getImageQueueDepth(),
-                        mHandler);
+                        backgroundExecutor);
 
         tryUpdateRelativeRotation(cameraId);
         mImageReader.setOnImageAvailableListener(
@@ -272,24 +275,30 @@
                     @Override
                     public void onImageAvailable(ImageReaderProxy imageReader) {
                         Analyzer analyzer = mSubscribedAnalyzer.get();
-                        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;
-                            }
+                        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());
+                                    if (analyzer != null) {
+                                        analyzer.analyze(image, mRelativeRotation.get());
+                                    }
+                                }
                             }
-                        }
+                        });
                     }
                 },
-                mHandler);
+                backgroundExecutor);
 
         SessionConfig.Builder sessionConfigBuilder = SessionConfig.Builder.createFrom(config);
 
@@ -329,12 +338,12 @@
         ACQUIRE_NEXT_IMAGE,
     }
 
-    /** Interface for analyzing images.
+    /**
+     * Interface for analyzing images.
      *
      * <p>Implement Analyzer and pass it to {@link ImageAnalysis#setAnalyzer(Analyzer)} to receive
      * images and perform custom processing by implementing the
      * {@link ImageAnalysis.Analyzer#analyze(ImageProxy, int)} function.
-     *
      */
     public interface Analyzer {
         /**
@@ -359,7 +368,6 @@
          * @param rotationDegrees The rotation which if applied to the image would make it match
          *                        the current target rotation of {@link ImageAnalysis}, expressed in
          *                        degrees in the range {@code [0..360)}.
-         *
          */
         void analyze(ImageProxy image, int rotationDegrees);
     }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageReaderProxy.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageReaderProxy.java
index b98161e..0cff10d 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageReaderProxy.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageReaderProxy.java
@@ -20,10 +20,13 @@
 import android.os.Handler;
 import android.view.Surface;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.RestrictTo.Scope;
 
+import java.util.concurrent.Executor;
+
 /**
  * An image reader proxy which has an analogous interface as {@link ImageReader}.
  *
@@ -98,10 +101,20 @@
      * <p>@see {@link ImageReader#setOnImageAvailableListener}.
      */
     void setOnImageAvailableListener(
-            @Nullable ImageReaderProxy.OnImageAvailableListener listener,
+            @NonNull ImageReaderProxy.OnImageAvailableListener listener,
             @Nullable Handler handler);
 
     /**
+     * Sets the on-image-available listener.
+     *
+     * @param listener The listener that will be run.
+     * @param executor The executor on which the listener should be invoked.
+     */
+    void setOnImageAvailableListener(
+            @NonNull ImageReaderProxy.OnImageAvailableListener listener,
+            @NonNull Executor executor);
+
+    /**
      * A listener for newly available images.
      *
      * @hide
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageReaderProxys.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageReaderProxys.java
index 6691a67..5d15561 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageReaderProxys.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageReaderProxys.java
@@ -18,7 +18,6 @@
 
 import android.graphics.ImageFormat;
 import android.media.ImageReader;
-import android.os.Handler;
 import android.util.Log;
 import android.util.Size;
 
@@ -29,6 +28,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 /**
  * Different implementations of {@link ImageReaderProxy}.
@@ -42,7 +42,7 @@
     private static final int SHARED_MAX_IMAGES = 8;
     static final List<QueuedImageReaderProxy> sSharedImageReaderProxys = new ArrayList<>();
     private static Set<DeviceProperties> sSharedReaderWhitelist;
-    private static ImageReader sSharedImageReader;
+    private static ImageReaderProxy sSharedImageReader;
 
     private ImageReaderProxys() {
     }
@@ -55,15 +55,16 @@
      * @param height    of the reader
      * @param format    of the reader
      * @param maxImages of the reader
-     * @param handler   for on-image-available callbacks
+     * @param executor  for on-image-available callbacks
      * @return new {@link ImageReaderProxy} instance
      */
     static ImageReaderProxy createCompatibleReader(
-            String cameraId, int width, int height, int format, int maxImages, Handler handler) {
+            String cameraId, int width, int height, int format, int maxImages,
+            Executor executor) {
         if (inSharedReaderWhitelist(DeviceProperties.create())) {
-            return createSharedReader(cameraId, width, height, format, maxImages, handler);
+            return createSharedReader(cameraId, width, height, format, maxImages, executor);
         } else {
-            return createIsolatedReader(width, height, format, maxImages, handler);
+            return createIsolatedReader(width, height, format, maxImages);
         }
     }
 
@@ -74,11 +75,10 @@
      * @param height    of the reader
      * @param format    of the reader
      * @param maxImages of the reader
-     * @param handler   for on-image-available callbacks
      * @return new {@link ImageReaderProxy} instance
      */
     public static ImageReaderProxy createIsolatedReader(
-            int width, int height, int format, int maxImages, Handler handler) {
+            int width, int height, int format, int maxImages) {
         ImageReader imageReader = ImageReader.newInstance(width, height, format, maxImages);
         return new AndroidImageReaderProxy(imageReader);
     }
@@ -91,21 +91,22 @@
      * @param height    of the reader
      * @param format    of the reader
      * @param maxImages of the reader
-     * @param handler   for on-image-available callbacks
+     * @param executor  for on-image-available callbacks
      * @return new {@link ImageReaderProxy} instance
      */
     public static ImageReaderProxy createSharedReader(
-            String cameraId, int width, int height, int format, int maxImages, Handler handler) {
+            String cameraId, int width, int height, int format, int maxImages,
+            Executor executor) {
         if (sSharedImageReader == null) {
             Size resolution =
                     CameraX.getSurfaceManager().getMaxOutputSize(cameraId, SHARED_IMAGE_FORMAT);
             Log.d(TAG, "Resolution of base ImageReader: " + resolution);
             sSharedImageReader =
-                    ImageReader.newInstance(
+                    new AndroidImageReaderProxy(ImageReader.newInstance(
                             resolution.getWidth(),
                             resolution.getHeight(),
                             SHARED_IMAGE_FORMAT,
-                            SHARED_MAX_IMAGES);
+                            SHARED_MAX_IMAGES));
         }
         Log.d(TAG, "Resolution of forked ImageReader: " + new Size(width, height));
         QueuedImageReaderProxy imageReaderProxy =
@@ -113,7 +114,7 @@
                         width, height, format, maxImages, sSharedImageReader.getSurface());
         sSharedImageReaderProxys.add(imageReaderProxy);
         sSharedImageReader.setOnImageAvailableListener(
-                new ForwardingImageReaderListener(sSharedImageReaderProxys), handler);
+                new ForwardingImageReaderListener(sSharedImageReaderProxys), executor);
         imageReaderProxy.addOnReaderCloseListener(
                 new QueuedImageReaderProxy.OnReaderCloseListener() {
                     @Override
@@ -151,7 +152,6 @@
 
     static void clearSharedReaders() {
         sSharedImageReaderProxys.clear();
-        sSharedImageReader.setOnImageAvailableListener(null, null);
         sSharedImageReader.close();
         sSharedImageReader = null;
     }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/MetadataImageReader.java b/camera/camera-core/src/main/java/androidx/camera/core/MetadataImageReader.java
index 184d08a9..4c59753 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/MetadataImageReader.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/MetadataImageReader.java
@@ -25,10 +25,12 @@
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.core.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * An {@link ImageReaderProxy} which matches the incoming {@link android.media.Image} with its
@@ -76,7 +78,7 @@
 
     @GuardedBy("mLock")
     @Nullable
-    private Handler mHandler;
+    private Executor mExecutor;
 
     /** ImageInfos haven't been matched with Image. */
     @GuardedBy("mLock")
@@ -111,7 +113,7 @@
         mImageReaderProxy = new AndroidImageReaderProxy(
                 ImageReader.newInstance(width, height, format, maxImages));
 
-        init(handler);
+        init(CameraXExecutors.newHandlerExecutor(handler));
     }
 
     /**
@@ -125,12 +127,12 @@
     MetadataImageReader(ImageReaderProxy imageReaderProxy, @Nullable Handler handler) {
         mImageReaderProxy = imageReaderProxy;
 
-        init(handler);
+        init(CameraXExecutors.newHandlerExecutor(handler));
     }
 
-    private void init(Handler handler) {
-        mHandler = handler;
-        mImageReaderProxy.setOnImageAvailableListener(mTransformedListener, handler);
+    private void init(Executor executor) {
+        mExecutor = executor;
+        mImageReaderProxy.setOnImageAvailableListener(mTransformedListener, executor);
         mImageProxiesIndex = 0;
         mMatchedImageProxies = new ArrayList<>(getMaxImages());
     }
@@ -241,12 +243,18 @@
 
     @Override
     public void setOnImageAvailableListener(
-            @Nullable final ImageReaderProxy.OnImageAvailableListener listener,
+            @NonNull final ImageReaderProxy.OnImageAvailableListener listener,
             @Nullable Handler handler) {
+        setOnImageAvailableListener(listener, CameraXExecutors.newHandlerExecutor(handler));
+    }
+
+    @Override
+    public void setOnImageAvailableListener(@NonNull OnImageAvailableListener listener,
+            @NonNull Executor executor) {
         synchronized (mLock) {
             mListener = listener;
-            mHandler = handler;
-            mImageReaderProxy.setOnImageAvailableListener(mTransformedListener, handler);
+            mExecutor = executor;
+            mImageReaderProxy.setOnImageAvailableListener(mTransformedListener, executor);
         }
     }
 
@@ -263,8 +271,8 @@
                 image.addOnImageCloseListener(this);
                 mMatchedImageProxies.add(image);
                 if (mListener != null) {
-                    if (mHandler != null) {
-                        mHandler.post(
+                    if (mExecutor != null) {
+                        mExecutor.execute(
                                 new Runnable() {
                                     @Override
                                     public void run() {
@@ -295,11 +303,6 @@
         }
     }
 
-    @Nullable
-    Handler getHandler() {
-        return mHandler;
-    }
-
     // Return the necessary CameraCaptureCallback, which needs to register to capture session.
     CameraCaptureCallback getCameraCaptureCallback() {
         return mCameraCaptureCallback;
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ProcessingImageReader.java b/camera/camera-core/src/main/java/androidx/camera/core/ProcessingImageReader.java
index c7e2935..966c0f3 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ProcessingImageReader.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ProcessingImageReader.java
@@ -16,7 +16,6 @@
 
 package androidx.camera.core;
 
-import android.media.Image;
 import android.media.ImageReader;
 import android.os.Handler;
 import android.util.Log;
@@ -34,6 +33,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * An {@link ImageReaderProxy} which takes one or more {@link android.media.Image}, processes it,
@@ -59,13 +59,13 @@
             };
 
     // Callback when Image is ready from OutputImageReader.
-    private ImageReader.OnImageAvailableListener mImageProcessedListener =
-            new ImageReader.OnImageAvailableListener() {
+    private ImageReaderProxy.OnImageAvailableListener mImageProcessedListener =
+            new ImageReaderProxy.OnImageAvailableListener() {
                 @Override
-                public void onImageAvailable(ImageReader reader) {
+                public void onImageAvailable(ImageReaderProxy reader) {
                     // Callback the output OnImageAvailableListener.
-                    if (mHandler != null) {
-                        mHandler.post(
+                    if (mExecutor != null) {
+                        mExecutor.execute(
                                 new Runnable() {
                                     @Override
                                     public void run() {
@@ -102,7 +102,7 @@
     private final ImageReaderProxy mInputImageReader;
 
     @GuardedBy("mLock")
-    private final ImageReader mOutputImageReader;
+    private final ImageReaderProxy mOutputImageReader;
 
     @GuardedBy("mLock")
     @Nullable
@@ -110,7 +110,7 @@
 
     @GuardedBy("mLock")
     @Nullable
-    Handler mHandler;
+    Executor mExecutor;
 
     @NonNull
     CaptureProcessor mCaptureProcessor;
@@ -127,10 +127,10 @@
      * @param height           Height of the ImageReader
      * @param format           Image format
      * @param maxImages        Maximum Image number the ImageReader can hold. The capacity should
-     *                        be greater than the captureBundle size in order to hold all the
+     *                         be greater than the captureBundle size in order to hold all the
      *                         Images needed with this processing.
      * @param handler          Handler for executing
-     * {@link ImageReaderProxy.OnImageAvailableListener}
+     *                         {@link ImageReaderProxy.OnImageAvailableListener}
      * @param captureBundle    The {@link CaptureBundle} includes the processing information
      * @param captureProcessor The {@link CaptureProcessor} to be invoked when the Images are ready
      */
@@ -143,9 +143,10 @@
                 format,
                 maxImages,
                 handler);
-        mOutputImageReader = ImageReader.newInstance(width, height, format, maxImages);
+        mOutputImageReader = new AndroidImageReaderProxy(
+                ImageReader.newInstance(width, height, format, maxImages));
 
-        init(handler, captureBundle, captureProcessor);
+        init(CameraXExecutors.newHandlerExecutor(handler), captureBundle, captureProcessor);
     }
 
     ProcessingImageReader(ImageReaderProxy imageReader, @Nullable Handler handler,
@@ -156,17 +157,19 @@
                     "MetadataImageReader is smaller than CaptureBundle.");
         }
         mInputImageReader = imageReader;
-        mOutputImageReader = ImageReader.newInstance(imageReader.getWidth(),
-                imageReader.getHeight(), imageReader.getImageFormat(), imageReader.getMaxImages());
+        mOutputImageReader = new AndroidImageReaderProxy(
+                ImageReader.newInstance(imageReader.getWidth(),
+                        imageReader.getHeight(), imageReader.getImageFormat(),
+                        imageReader.getMaxImages()));
 
-        init(handler, captureBundle, captureProcessor);
+        init(CameraXExecutors.newHandlerExecutor(handler), captureBundle, captureProcessor);
     }
 
-    private void init(@Nullable Handler handler, @NonNull CaptureBundle captureBundle,
+    private void init(@NonNull Executor executor, @NonNull CaptureBundle captureBundle,
             @NonNull CaptureProcessor captureProcessor) {
-        mHandler = handler;
-        mInputImageReader.setOnImageAvailableListener(mTransformedListener, handler);
-        mOutputImageReader.setOnImageAvailableListener(mImageProcessedListener, handler);
+        mExecutor = executor;
+        mInputImageReader.setOnImageAvailableListener(mTransformedListener, executor);
+        mOutputImageReader.setOnImageAvailableListener(mImageProcessedListener, executor);
         mCaptureProcessor = captureProcessor;
         mCaptureProcessor.onOutputSurface(mOutputImageReader.getSurface(), getImageFormat());
         mCaptureProcessor.onResolutionUpdate(
@@ -179,12 +182,7 @@
     @Nullable
     public ImageProxy acquireLatestImage() {
         synchronized (mLock) {
-            Image image = mOutputImageReader.acquireLatestImage();
-            if (image == null) {
-                return null;
-            }
-
-            return new AndroidImageProxy(image);
+            return mOutputImageReader.acquireLatestImage();
         }
     }
 
@@ -192,12 +190,7 @@
     @Nullable
     public ImageProxy acquireNextImage() {
         synchronized (mLock) {
-            Image image = mOutputImageReader.acquireNextImage();
-            if (image == null) {
-                return null;
-            }
-
-            return new AndroidImageProxy(image);
+            return mOutputImageReader.acquireNextImage();
         }
     }
 
@@ -252,13 +245,20 @@
 
     @Override
     public void setOnImageAvailableListener(
-            @Nullable final ImageReaderProxy.OnImageAvailableListener listener,
+            @NonNull final ImageReaderProxy.OnImageAvailableListener listener,
             @Nullable Handler handler) {
+        setOnImageAvailableListener(listener, CameraXExecutors.newHandlerExecutor(handler));
+    }
+
+    @Override
+    public void setOnImageAvailableListener(@NonNull OnImageAvailableListener listener,
+            @NonNull Executor executor) {
+        // TODO(b/115747543) support callback on executor
         synchronized (mLock) {
             mListener = listener;
-            mHandler = handler;
-            mInputImageReader.setOnImageAvailableListener(mTransformedListener, handler);
-            mOutputImageReader.setOnImageAvailableListener(mImageProcessedListener, handler);
+            mExecutor = executor;
+            mInputImageReader.setOnImageAvailableListener(mTransformedListener, executor);
+            mOutputImageReader.setOnImageAvailableListener(mImageProcessedListener, executor);
         }
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/QueuedImageReaderProxy.java b/camera/camera-core/src/main/java/androidx/camera/core/QueuedImageReaderProxy.java
index ac42f16..cbcc54d 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/QueuedImageReaderProxy.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/QueuedImageReaderProxy.java
@@ -20,12 +20,15 @@
 import android.view.Surface;
 
 import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 /**
  * An {@link ImageReaderProxy} which maintains a queue of recently available images.
@@ -63,7 +66,7 @@
     private ImageReaderProxy.OnImageAvailableListener mOnImageAvailableListener;
     @GuardedBy("this")
     @Nullable
-    private Handler mOnImageAvailableHandler;
+    private Executor mOnImageAvailableExecutor;
     @GuardedBy("this")
     private boolean mClosed;
 
@@ -151,9 +154,9 @@
         if (mImages.size() < mMaxImages) {
             mImages.add(image);
             image.addOnImageCloseListener(this);
-            if (mOnImageAvailableListener != null && mOnImageAvailableHandler != null) {
+            if (mOnImageAvailableListener != null && mOnImageAvailableExecutor != null) {
                 final OnImageAvailableListener listener = mOnImageAvailableListener;
-                mOnImageAvailableHandler.post(
+                mOnImageAvailableExecutor.execute(
                         new Runnable() {
                             @Override
                             public void run() {
@@ -171,7 +174,8 @@
     @Override
     public synchronized void close() {
         if (!mClosed) {
-            setOnImageAvailableListener(null, null);
+            this.mOnImageAvailableExecutor = null;
+            this.mOnImageAvailableListener = null;
             // We need to copy into a different list, because closing an image triggers the on-close
             // listener which in turn modifies the original list.
             List<ImageProxy> imagesToClose = new ArrayList<>(mImages);
@@ -216,11 +220,20 @@
 
     @Override
     public synchronized void setOnImageAvailableListener(
-            @Nullable OnImageAvailableListener onImageAvailableListener,
+            @NonNull OnImageAvailableListener onImageAvailableListener,
             @Nullable Handler onImageAvailableHandler) {
+        setOnImageAvailableListener(onImageAvailableListener,
+                onImageAvailableHandler == null ? null :
+                        CameraXExecutors.newHandlerExecutor(onImageAvailableHandler));
+    }
+
+    @Override
+    public synchronized void setOnImageAvailableListener(
+            @NonNull OnImageAvailableListener onImageAvailableListener,
+            @NonNull Executor executor) {
         throwExceptionIfClosed();
         mOnImageAvailableListener = onImageAvailableListener;
-        mOnImageAvailableHandler = onImageAvailableHandler;
+        mOnImageAvailableExecutor = executor;
     }
 
     @Override
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/CameraIdFilterSetTest.java b/camera/camera-core/src/test/java/androidx/camera/core/CameraIdFilterSetTest.java
index 5d799db..40ffaad 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/CameraIdFilterSetTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/CameraIdFilterSetTest.java
@@ -18,17 +18,21 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.os.Build;
+
 import androidx.camera.testing.fakes.FakeCameraIdFilter;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
 import org.robolectric.annotation.internal.DoNotInstrument;
 
 @SmallTest
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 public class CameraIdFilterSetTest {
     private CameraIdFilterSet mCameraIdFilterSet = new CameraIdFilterSet();
 
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/CaptureBundlesTest.java b/camera/camera-core/src/test/java/androidx/camera/core/CaptureBundlesTest.java
index b6bcc13..f6a205f 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/CaptureBundlesTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/CaptureBundlesTest.java
@@ -18,16 +18,20 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.os.Build;
+
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
 import org.robolectric.annotation.internal.DoNotInstrument;
 
 @SmallTest
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 public class CaptureBundlesTest {
     @Test
     public void singleDefaultBundleSizeCheck() {
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/CaptureStagesTest.java b/camera/camera-core/src/test/java/androidx/camera/core/CaptureStagesTest.java
index 7f7ab60..fdc808e 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/CaptureStagesTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/CaptureStagesTest.java
@@ -18,21 +18,26 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.os.Build;
+
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
 import org.robolectric.annotation.internal.DoNotInstrument;
 
 @SmallTest
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 public class CaptureStagesTest {
     @Test
     public void defaultCaptureStageHasNoOptions() {
         CaptureStage captureStage = new CaptureStage.DefaultCaptureStage();
-        Config options = captureStage.getCaptureConfig().getImplementationOptions();
+        androidx.camera.core.Config options =
+                captureStage.getCaptureConfig().getImplementationOptions();
 
         assertThat(options.listOptions().size()).isEqualTo(0);
     }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageUtilTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageUtilTest.java
index e89270b..e0e88d1 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageUtilTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageUtilTest.java
@@ -28,6 +28,7 @@
 import android.graphics.ImageFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.Build;
 import android.util.Base64;
 import android.util.Rational;
 import android.util.Size;
@@ -40,6 +41,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
 import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.nio.ByteBuffer;
@@ -47,6 +49,7 @@
 @SmallTest
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 public class ImageUtilTest {
     private static final int WIDTH = 160;
     private static final int HEIGHT = 120;
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ProcessingImageReaderTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ProcessingImageReaderTest.java
index dfa5d8b..552efbf 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ProcessingImageReaderTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ProcessingImageReaderTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.mock;
 
 import android.graphics.ImageFormat;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Size;
@@ -34,6 +35,7 @@
 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.shadows.ShadowLooper;
 
@@ -47,6 +49,7 @@
 @SmallTest
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 public final class ProcessingImageReaderTest {
     private static final int CAPTURE_ID_0 = 0;
     private static final int CAPTURE_ID_1 = 1;
@@ -88,7 +91,7 @@
     }
 
     @Test
-    public void canSetFuturesInSettableImageProxyBundle()
+    public void canSetFuturesInSettableImawgeProxyBundle()
             throws InterruptedException, TimeoutException, ExecutionException {
         final AtomicReference<ImageProxyBundle> bundleRef = new AtomicReference<>();
         // Sets the callback from ProcessingImageReader to start processing
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/SingleImageProxyBundleTest.java b/camera/camera-core/src/test/java/androidx/camera/core/SingleImageProxyBundleTest.java
index 61ac5ef..a8845df 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/SingleImageProxyBundleTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/SingleImageProxyBundleTest.java
@@ -18,6 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.os.Build;
+
 import androidx.camera.testing.fakes.FakeImageInfo;
 import androidx.camera.testing.fakes.FakeImageProxy;
 import androidx.test.filters.SmallTest;
@@ -25,11 +27,13 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
 import org.robolectric.annotation.internal.DoNotInstrument;
 
 @SmallTest
 @RunWith(RobolectricTestRunner.class)
 @DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 public final class SingleImageProxyBundleTest {
     @Test
     public void successfulCreationFromImageProxy() {
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ExtensionAvailability.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ExtensionAvailability.java
deleted file mode 100644
index 252d8ec..0000000
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ExtensionAvailability.java
+++ /dev/null
@@ -1,37 +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.camera.extensions.impl;
-
-import android.hardware.camera2.CameraCharacteristics;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * Provides abstract methods that the OEM needs to implement to check extension status.
- */
-public interface ExtensionAvailability {
-    /**
-     * Indicates whether the extension is supported on the camera device with specified camera id.
-     *
-     * @param cameraId              The camera2 id string of the camera.
-     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
-     * @return true if the extension is supported, otherwise false
-     */
-    boolean isExtensionAvailable(@NonNull String cameraId,
-            @Nullable CameraCharacteristics cameraCharacteristics);
-}
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
index 8e2773f..6c5c124 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
@@ -23,7 +23,16 @@
 /**
  * Provides abstract methods that the OEM needs to implement to enable extensions for image capture.
  */
-public interface ImageCaptureExtenderImpl extends ExtenderStateListener, ExtensionAvailability {
+public interface ImageCaptureExtenderImpl extends ExtenderStateListener {
+    /**
+     * Indicates whether the extension is supported on the device.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     * @return true if the extension is supported, otherwise false
+     */
+    boolean isExtensionAvailable(String cameraId, CameraCharacteristics cameraCharacteristics);
+
     /**
      * Initializes the extender to be used with the specified camera.
      *
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
index 215ddc4..cfe2995 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
@@ -22,7 +22,7 @@
 /**
  * Provides abstract methods that the OEM needs to implement to enable extensions in the preview.
  */
-public interface PreviewExtenderImpl extends ExtenderStateListener, ExtensionAvailability {
+public interface PreviewExtenderImpl extends ExtenderStateListener {
     /** The different types of the preview processing. */
     enum ProcessorType {
         /** Processor which only updates the {@link CaptureStageImpl}. */
@@ -34,6 +34,15 @@
     }
 
     /**
+     * Indicates whether the extension is supported on the device.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     * @return true if the extension is supported, otherwise false
+     */
+    boolean isExtensionAvailable(String cameraId, CameraCharacteristics cameraCharacteristics);
+
+    /**
      * Initializes the extender to be used with the specified camera.
      *
      * <p>This should be called before any other method on the extender. The exception is {@link
diff --git a/camera/camera-extensions/build.gradle b/camera/camera-extensions/build.gradle
index 5e20eb3..0e3202c 100644
--- a/camera/camera-extensions/build.gradle
+++ b/camera/camera-extensions/build.gradle
@@ -57,8 +57,7 @@
     defaultConfig {
         minSdkVersion 21
 
-        // TODO: To use the separate version for extensions after new library item is created.
-        buildConfigField "String", "CAMERA_VERSION", "\"${LibraryVersions.CAMERA}\""
+        buildConfigField "String", "CAMERA_VERSION", "\"${LibraryVersions.CAMERA_EXTENSIONS}\""
     }
 }
 
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionTest.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionTest.java
new file mode 100644
index 0000000..471b902
--- /dev/null
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionTest.java
@@ -0,0 +1,315 @@
+/*
+ * 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.extensions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.Manifest;
+import android.content.Context;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+
+import androidx.annotation.NonNull;
+import androidx.camera.camera2.Camera2AppConfig;
+import androidx.camera.camera2.Camera2Config;
+import androidx.camera.camera2.impl.CameraEventCallback;
+import androidx.camera.camera2.impl.CameraEventCallbacks;
+import androidx.camera.core.AppConfig;
+import androidx.camera.core.CameraInfoUnavailableException;
+import androidx.camera.core.CameraX;
+import androidx.camera.core.ImageCapture;
+import androidx.camera.core.ImageCaptureConfig;
+import androidx.camera.core.ImageProxy;
+import androidx.camera.core.Preview;
+import androidx.camera.core.PreviewConfig;
+import androidx.camera.extensions.impl.AutoPreviewExtenderImpl;
+import androidx.camera.extensions.impl.BeautyPreviewExtenderImpl;
+import androidx.camera.extensions.impl.BokehPreviewExtenderImpl;
+import androidx.camera.extensions.impl.HdrPreviewExtenderImpl;
+import androidx.camera.extensions.impl.NightPreviewExtenderImpl;
+import androidx.camera.testing.CameraUtil;
+import androidx.camera.testing.fakes.FakeLifecycleOwner;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.GrantPermissionRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class ExtensionTest {
+
+    @Rule
+    public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(
+            Manifest.permission.CAMERA);
+
+    private FakeLifecycleOwner mLifecycleOwner;
+    private CameraDevice.StateCallback mCameraStatusCallback;
+    private ExtensionsManager.EffectMode mEffectMode;
+    private CameraX.LensFacing mLensFacing;
+    private CountDownLatch mLatch;
+
+    private ImageCaptureConfig.Builder mImageCaptureConfigBuilder;
+    private PreviewConfig.Builder mPreviewConfigBuilder;
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> getParameters() {
+        return Arrays.asList(new Object[][] {
+                { ExtensionsManager.EffectMode.BOKEH, CameraX.LensFacing.FRONT },
+                { ExtensionsManager.EffectMode.BOKEH, CameraX.LensFacing.BACK },
+                { ExtensionsManager.EffectMode.HDR, CameraX.LensFacing.FRONT },
+                { ExtensionsManager.EffectMode.HDR, CameraX.LensFacing.BACK },
+                { ExtensionsManager.EffectMode.BEAUTY, CameraX.LensFacing.FRONT },
+                { ExtensionsManager.EffectMode.BEAUTY, CameraX.LensFacing.BACK },
+                { ExtensionsManager.EffectMode.NIGHT, CameraX.LensFacing.FRONT },
+                { ExtensionsManager.EffectMode.NIGHT, CameraX.LensFacing.BACK },
+                { ExtensionsManager.EffectMode.AUTO, CameraX.LensFacing.FRONT },
+                { ExtensionsManager.EffectMode.AUTO, CameraX.LensFacing.BACK }
+        });
+    }
+
+    public ExtensionTest(ExtensionsManager.EffectMode effectMode, CameraX.LensFacing lensFacing) {
+        mEffectMode = effectMode;
+        mLensFacing = lensFacing;
+    }
+
+    @Before
+    public void setUp() {
+        assumeTrue(CameraUtil.deviceHasCamera());
+
+        Context context = ApplicationProvider.getApplicationContext();
+        AppConfig appConfig = Camera2AppConfig.create(context);
+        CameraX.init(context, appConfig);
+
+        mLifecycleOwner = new FakeLifecycleOwner();
+        mLifecycleOwner.startAndResume();
+
+        mImageCaptureConfigBuilder = new ImageCaptureConfig.Builder();
+        mPreviewConfigBuilder = new PreviewConfig.Builder();
+        mCameraStatusCallback = new CameraDevice.StateCallback() {
+            @Override
+            public void onOpened(@NonNull CameraDevice camera) {
+                mLatch  = new CountDownLatch(1);
+            }
+
+            @Override
+            public void onDisconnected(@NonNull CameraDevice camera) {
+
+            }
+
+            @Override
+            public void onError(@NonNull CameraDevice camera, int error) {
+
+            }
+
+            @Override
+            public void onClosed(@NonNull CameraDevice camera) {
+                mLatch.countDown();
+            }
+        };
+
+        new Camera2Config.Extender(mImageCaptureConfigBuilder).setDeviceStateCallback(
+                mCameraStatusCallback);
+    }
+
+    @After
+    public void cleanUp() throws InterruptedException {
+        if (mLatch != null) {
+            CameraX.unbindAll();
+
+            // Make sure camera was closed.
+            mLatch.await(3000, TimeUnit.MILLISECONDS);
+        }
+    }
+
+    @Test
+    public void testCanBindToLifeCycleAndTakePicture() {
+        assumeTrue(CameraUtil.hasCameraWithLensFacing(mLensFacing));
+        assumeTrue(ExtensionsManager.isExtensionAvailable(mEffectMode, mLensFacing));
+        assumeTrue(supportAFMode(mLensFacing));
+
+        enableExtension(mEffectMode, mLensFacing);
+
+        Preview.OnPreviewOutputUpdateListener mockOnPreviewOutputUpdateListener = mock(
+                Preview.OnPreviewOutputUpdateListener.class);
+        ImageCapture.OnImageCapturedListener mockOnImageCapturedListener = mock(
+                ImageCapture.OnImageCapturedListener.class);
+
+        // To test bind/unbind and take picture.
+        ImageCapture imageCapture = new ImageCapture(mImageCaptureConfigBuilder.build());
+        Preview preview = new Preview(mPreviewConfigBuilder.build());
+
+        CameraX.bindToLifecycle(mLifecycleOwner, preview, imageCapture);
+
+        // To set the update listener and Preview will change to active state.
+        preview.setOnPreviewOutputUpdateListener(mockOnPreviewOutputUpdateListener);
+
+        imageCapture.takePicture(mockOnImageCapturedListener);
+
+        // Verify the image captured.
+        ArgumentCaptor<ImageProxy> imageProxy = ArgumentCaptor.forClass(ImageProxy.class);
+        verify(mockOnImageCapturedListener, timeout(3000)).onCaptureSuccess(
+                imageProxy.capture(), anyInt());
+        assertNotNull(imageProxy.getValue());
+        imageProxy.getValue().close(); // Close the image after verification.
+
+        // Verify the take picture should not have any error happen.
+        verify(mockOnImageCapturedListener, never()).onError(
+                any(ImageCapture.UseCaseError.class), anyString(), any(Throwable.class));
+    }
+
+    @Test
+    public void testEventCallbackInConfig() {
+        assumeTrue(CameraUtil.hasCameraWithLensFacing(mLensFacing));
+        assumeTrue(ExtensionsManager.isExtensionAvailable(mEffectMode, mLensFacing));
+
+        enableExtension(mEffectMode, mLensFacing);
+
+        // Verify Preview config should have related callback.
+        PreviewConfig previewConfig = mPreviewConfigBuilder.build();
+        assertNotNull(previewConfig.getUseCaseEventListener());
+        CameraEventCallbacks callback1 = new Camera2Config(previewConfig).getCameraEventCallback(
+                null);
+        assertNotNull(callback1);
+        assertEquals(callback1.getAllItems().size(), 1);
+        assertThat(callback1.getAllItems().get(0)).isInstanceOf(CameraEventCallback.class);
+
+        // Verify ImageCapture config should have related callback.
+        ImageCaptureConfig imageCaptureConfig = mImageCaptureConfigBuilder.build();
+        assertNotNull(imageCaptureConfig.getUseCaseEventListener());
+        assertNotNull(imageCaptureConfig.getCaptureBundle());
+        CameraEventCallbacks callback2 = new Camera2Config(
+                imageCaptureConfig).getCameraEventCallback(null);
+        assertNotNull(callback2);
+        assertEquals(callback2.getAllItems().size(), 1);
+        assertThat(callback2.getAllItems().get(0)).isInstanceOf(CameraEventCallback.class);
+    }
+
+    /**
+     * To invoke the enableExtension() method for different effect.
+     */
+    private void enableExtension(ExtensionsManager.EffectMode effectMode,
+            CameraX.LensFacing lensFacing) {
+
+        mImageCaptureConfigBuilder.setLensFacing(lensFacing);
+        mPreviewConfigBuilder.setLensFacing(lensFacing);
+
+        ImageCaptureExtender imageCaptureExtender = null;
+        PreviewExtender previewExtender = null;
+
+        switch (effectMode) {
+            case HDR:
+                imageCaptureExtender = HdrImageCaptureExtender.create(mImageCaptureConfigBuilder);
+                previewExtender = HdrPreviewExtender.create(mPreviewConfigBuilder);
+
+                // Make sure we are testing on the testlib/Vendor implementation.
+                assertThat(previewExtender.mImpl).isInstanceOf(HdrPreviewExtenderImpl.class);
+                break;
+            case BOKEH:
+                imageCaptureExtender = BokehImageCaptureExtender.create(
+                        mImageCaptureConfigBuilder);
+                previewExtender = BokehPreviewExtender.create(mPreviewConfigBuilder);
+
+                // Make sure we are testing on the testlib/Vendor implementation.
+                assertThat(previewExtender.mImpl).isInstanceOf(BokehPreviewExtenderImpl.class);
+                break;
+            case BEAUTY:
+                imageCaptureExtender = BeautyImageCaptureExtender.create(
+                        mImageCaptureConfigBuilder);
+                previewExtender = BeautyPreviewExtender.create(mPreviewConfigBuilder);
+
+                // Make sure we are testing on the testlib/Vendor implementation.
+                assertThat(previewExtender.mImpl).isInstanceOf(BeautyPreviewExtenderImpl.class);
+                break;
+            case NIGHT:
+                imageCaptureExtender = NightImageCaptureExtender.create(mImageCaptureConfigBuilder);
+                previewExtender = NightPreviewExtender.create(mPreviewConfigBuilder);
+
+                // Make sure we are testing on the testlib/Vendor implementation.
+                assertThat(previewExtender.mImpl).isInstanceOf(NightPreviewExtenderImpl.class);
+                break;
+            case AUTO:
+                imageCaptureExtender = AutoImageCaptureExtender.create(mImageCaptureConfigBuilder);
+                previewExtender = AutoPreviewExtender.create(mPreviewConfigBuilder);
+
+                // Make sure we are testing on the testlib/Vendor implementation.
+                assertThat(previewExtender.mImpl).isInstanceOf(AutoPreviewExtenderImpl.class);
+                break;
+        }
+
+        assertNotNull(imageCaptureExtender);
+        assertNotNull(previewExtender);
+
+        assertTrue(previewExtender.isExtensionAvailable());
+        previewExtender.enableExtension();
+        assertTrue(imageCaptureExtender.isExtensionAvailable());
+        imageCaptureExtender.enableExtension();
+    }
+
+    private static boolean supportAFMode(CameraX.LensFacing lensFacing) {
+        String cameraId = null;
+        try {
+            cameraId = CameraX.getCameraWithLensFacing(lensFacing);
+        } catch (CameraInfoUnavailableException e) {
+            return false;
+        }
+
+        CameraCharacteristics characteristics = null;
+        try {
+            characteristics = CameraUtil.getCameraManager().getCameraCharacteristics(cameraId);
+        } catch (CameraAccessException e) {
+            return false;
+        }
+
+        int[] afModes = characteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
+
+        if (afModes == null) {
+            return false;
+        }
+
+        // CameraX will use CONTROL_AF_MODE_AUTO or CONTROL_AF_MODE_CONTINUOUS_PICTURE to take
+        // picture. To check if the camera can support the these AF modes.
+        return Arrays.asList(afModes).contains(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
+                && Arrays.asList(afModes).contains(CaptureRequest.CONTROL_AF_MODE_AUTO);
+    }
+}
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
index 7ae0547..d9551c3 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/PreviewExtenderTest.java
@@ -33,6 +33,7 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.TotalCaptureResult;
+import android.media.Image;
 import android.util.Pair;
 
 import androidx.camera.core.CameraX;
@@ -48,6 +49,7 @@
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.GrantPermissionRule;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -61,6 +63,8 @@
 @RunWith(AndroidJUnit4.class)
 public class PreviewExtenderTest {
 
+    private FakeLifecycleOwner mFakeLifecycle;
+
     @Rule
     public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(
             Manifest.permission.CAMERA);
@@ -68,13 +72,19 @@
     @Before
     public void setUp() {
         assumeTrue(CameraUtil.deviceHasCamera());
+        assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraX.LensFacing.BACK));
+        mFakeLifecycle = new FakeLifecycleOwner();
+        mFakeLifecycle.startAndResume();
+    }
+
+    @After
+    public void cleanUp() {
+        CameraX.unbindAll();
     }
 
     @Test
     @MediumTest
     public void extenderLifeCycleTest_noMoreInvokeBeforeAndAfterInitDeInit() {
-        FakeLifecycleOwner lifecycle = new FakeLifecycleOwner();
-
         PreviewExtenderImpl mockPreviewExtenderImpl = mock(PreviewExtenderImpl.class);
         when(mockPreviewExtenderImpl.getProcessorType()).thenReturn(
                 PreviewExtenderImpl.ProcessorType.PROCESSOR_TYPE_IMAGE_PROCESSOR);
@@ -92,8 +102,7 @@
 
         Preview useCase = new Preview(configBuilder.build());
 
-        CameraX.bindToLifecycle(lifecycle, useCase);
-        lifecycle.startAndResume();
+        CameraX.bindToLifecycle(mFakeLifecycle, useCase);
 
         // To set the update listener and Preview will change to active state.
         useCase.setOnPreviewOutputUpdateListener(mock(Preview.OnPreviewOutputUpdateListener.class));
@@ -129,8 +138,6 @@
     @Test
     @MediumTest
     public void getCaptureStagesTest_shouldSetToRepeatingRequest() throws InterruptedException {
-        FakeLifecycleOwner lifecycle = new FakeLifecycleOwner();
-
         // Set up a result for getCaptureStages() testing.
         CaptureStageImpl fakeCaptureStageImpl = new FakeCaptureStageImpl();
 
@@ -154,13 +161,13 @@
                 mockPreviewExtenderImpl);
         fakePreviewExtender.enableExtension();
 
-        Preview useCase = new Preview(configBuilder.build());
+        Preview preview = new Preview(configBuilder.build());
 
-        CameraX.bindToLifecycle(lifecycle, useCase);
-        lifecycle.startAndResume();
+        CameraX.bindToLifecycle(mFakeLifecycle, preview);
 
         // To set the update listener and Preview will change to active state.
-        useCase.setOnPreviewOutputUpdateListener(mock(Preview.OnPreviewOutputUpdateListener.class));
+        preview.setOnPreviewOutputUpdateListener(
+                mock(Preview.OnPreviewOutputUpdateListener.class));
 
         ArgumentCaptor<TotalCaptureResult> captureResultArgumentCaptor = ArgumentCaptor.forClass(
                 TotalCaptureResult.class);
@@ -181,6 +188,36 @@
         }
     }
 
+    @Test
+    @MediumTest
+    public void processShouldBeInvoked_typeImageProcessor() {
+        // The type image processor will invoke PreviewImageProcessor.process()
+        PreviewImageProcessorImpl mockPreviewImageProcessorImpl = mock(
+                PreviewImageProcessorImpl.class);
+
+        PreviewExtenderImpl mockPreviewExtenderImpl = mock(PreviewExtenderImpl.class);
+        when(mockPreviewExtenderImpl.getProcessor()).thenReturn(mockPreviewImageProcessorImpl);
+        when(mockPreviewExtenderImpl.getProcessorType()).thenReturn(
+                PreviewExtenderImpl.ProcessorType.PROCESSOR_TYPE_IMAGE_PROCESSOR);
+        when(mockPreviewExtenderImpl.isExtensionAvailable(any(String.class),
+                any(CameraCharacteristics.class))).thenReturn(true);
+
+        PreviewConfig.Builder configBuilder = new PreviewConfig.Builder().setLensFacing(
+                CameraX.LensFacing.BACK);
+        FakePreviewExtender fakePreviewExtender = new FakePreviewExtender(configBuilder,
+                mockPreviewExtenderImpl);
+        fakePreviewExtender.enableExtension();
+        Preview preview = new Preview(configBuilder.build());
+        CameraX.bindToLifecycle(mFakeLifecycle, preview);
+        // To set the update listener and Preview will change to active state.
+        preview.setOnPreviewOutputUpdateListener(
+                mock(Preview.OnPreviewOutputUpdateListener.class));
+
+        // To verify the process() method was invoked with non-null TotalCaptureResult input.
+        verify(mockPreviewImageProcessorImpl, timeout(3000).atLeastOnce()).process(any(Image.class),
+                any(TotalCaptureResult.class));
+    }
+
     private class FakePreviewExtender extends PreviewExtender {
         FakePreviewExtender(PreviewConfig.Builder builder, PreviewExtenderImpl impl) {
             init(builder, impl, ExtensionsManager.EffectMode.NORMAL);
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionCameraIdFilter.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionCameraIdFilter.java
index 6dacde0..ce50789 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionCameraIdFilter.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionCameraIdFilter.java
@@ -17,20 +17,36 @@
 package androidx.camera.extensions;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.camera.core.CameraIdFilter;
-import androidx.camera.extensions.impl.ExtensionAvailability;
+import androidx.camera.extensions.impl.ImageCaptureExtenderImpl;
+import androidx.camera.extensions.impl.PreviewExtenderImpl;
 
 import java.util.LinkedHashSet;
 import java.util.Set;
 
 /**
- * Filter camera id by extension availability.
+ * Filter camera id by extender implementation. If the implementation is null, all the camera ids
+ * will be considered available.
  */
 public final class ExtensionCameraIdFilter implements CameraIdFilter {
-    private ExtensionAvailability mExtensionAvailability;
+    private PreviewExtenderImpl mPreviewExtenderImpl;
+    private ImageCaptureExtenderImpl mImageCaptureExtenderImpl;
 
-    ExtensionCameraIdFilter(ExtensionAvailability extensionAvailability) {
-        mExtensionAvailability = extensionAvailability;
+    ExtensionCameraIdFilter(@Nullable PreviewExtenderImpl previewExtenderImpl) {
+        mPreviewExtenderImpl = previewExtenderImpl;
+        mImageCaptureExtenderImpl = null;
+    }
+
+    ExtensionCameraIdFilter(@Nullable ImageCaptureExtenderImpl imageCaptureExtenderImpl) {
+        mPreviewExtenderImpl = null;
+        mImageCaptureExtenderImpl = imageCaptureExtenderImpl;
+    }
+
+    ExtensionCameraIdFilter(@Nullable PreviewExtenderImpl previewExtenderImpl,
+            @Nullable ImageCaptureExtenderImpl imageCaptureExtenderImpl) {
+        mPreviewExtenderImpl = previewExtenderImpl;
+        mImageCaptureExtenderImpl = imageCaptureExtenderImpl;
     }
 
     @Override
@@ -38,8 +54,22 @@
     public Set<String> filter(@NonNull Set<String> cameraIdSet) {
         Set<String> resultCameraIdSet = new LinkedHashSet<>();
         for (String cameraId : cameraIdSet) {
-            if (mExtensionAvailability.isExtensionAvailable(cameraId,
-                    CameraUtil.getCameraCharacteristics(cameraId))) {
+            boolean available = true;
+
+            // If preview extender impl isn't null, check if the camera id is supported.
+            if (mPreviewExtenderImpl != null) {
+                available = mPreviewExtenderImpl.isExtensionAvailable(cameraId,
+                        CameraUtil.getCameraCharacteristics(cameraId));
+            }
+
+            // If image capture extender impl isn't null, check if the camera id is supported.
+            if (mImageCaptureExtenderImpl != null) {
+                available = mImageCaptureExtenderImpl.isExtensionAvailable(cameraId,
+                        CameraUtil.getCameraCharacteristics(cameraId));
+            }
+
+            // If the camera id is supported, add it to the result set.
+            if (available) {
                 resultCameraIdSet.add(cameraId);
             }
         }
diff --git a/camera/camera-testing/build.gradle b/camera/camera-testing/build.gradle
index 396ab3c..2be49b7 100644
--- a/camera/camera-testing/build.gradle
+++ b/camera/camera-testing/build.gradle
@@ -30,7 +30,7 @@
     implementation("androidx.annotation:annotation:1.0.0")
     implementation(GUAVA_LISTENABLE_FUTURE)
     implementation(project(":camera:camera-core"))
-    implementation("androidx.core:core:1.1.0-rc01")
+    implementation("androidx.core:core:1.1.0")
     implementation("androidx.concurrent:concurrent-futures:1.0.0-alpha03")
     implementation(JUNIT)
 }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java b/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
index 09c3e56..ef6e389 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/CameraUtil.java
@@ -20,20 +20,25 @@
 import android.content.Context;
 import android.hardware.Camera;
 import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraDevice.StateCallback;
 import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RequiresPermission;
 import androidx.camera.core.BaseCamera;
+import androidx.camera.core.CameraX;
 import androidx.camera.core.UseCase;
 import androidx.test.core.app.ApplicationProvider;
 
 import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -191,4 +196,54 @@
 
         return numberOfCamera > 0;
     }
+
+    /**
+     * Check if the specified lensFacing is supported by the device.
+     *
+     * @param lensFacing The desired camera lensFacing.
+     * @return True if the device supports the lensFacing.
+     * @throws IllegalStateException if the CAMERA permission is not currently granted.
+     */
+    public static boolean hasCameraWithLensFacing(@NonNull CameraX.LensFacing lensFacing) {
+
+        CameraManager cameraManager = getCameraManager();
+
+        List<String> camerasList = null;
+        try {
+            camerasList = Arrays.asList(cameraManager.getCameraIdList());
+        } catch (CameraAccessException e) {
+            throw new IllegalStateException(
+                    "Unable to retrieve list of cameras on device.", e);
+        }
+
+        // Convert to from CameraX enum to Camera2 CameraMetadata
+        Integer lensFacingInteger = -1;
+        switch (lensFacing) {
+            case BACK:
+                lensFacingInteger = CameraMetadata.LENS_FACING_BACK;
+                break;
+            case FRONT:
+                lensFacingInteger = CameraMetadata.LENS_FACING_FRONT;
+                break;
+        }
+
+        for (String cameraId : camerasList) {
+            CameraCharacteristics characteristics = null;
+            try {
+                characteristics = cameraManager.getCameraCharacteristics(cameraId);
+            } catch (CameraAccessException e) {
+                throw new IllegalStateException(
+                        "Unable to retrieve info for camera with id " + cameraId + ".", e);
+            }
+            Integer cameraLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING);
+            if (cameraLensFacing == null) {
+                continue;
+            }
+            if (cameraLensFacing.equals(lensFacingInteger)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeImageReaderProxy.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeImageReaderProxy.java
index 8b6d560..212d783 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeImageReaderProxy.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeImageReaderProxy.java
@@ -30,6 +30,7 @@
 
 import java.util.NoSuchElementException;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
@@ -43,7 +44,7 @@
     private int mImageFormat = ImageFormat.JPEG;
     private final int mMaxImages;
     private Surface mSurface;
-    private Handler mHandler;
+    private Executor mExecutor;
 
     // Queue of all futures for ImageProxys which have not yet been closed.
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
@@ -131,10 +132,16 @@
 
     @Override
     public void setOnImageAvailableListener(
-            @Nullable final ImageReaderProxy.OnImageAvailableListener listener,
+            @NonNull final ImageReaderProxy.OnImageAvailableListener listener,
             @Nullable Handler handler) {
+        setOnImageAvailableListener(mListener, CameraXExecutors.newHandlerExecutor(handler));
+    }
+
+    @Override
+    public void setOnImageAvailableListener(@NonNull OnImageAvailableListener listener,
+            @NonNull Executor executor) {
         mListener = listener;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     public void setSurface(Surface surface) {
@@ -223,14 +230,14 @@
 
     private void triggerImageAvailableListener() {
         if (mListener != null) {
-            if (mHandler != null) {
-                mHandler.post(
-                        new Runnable() {
-                            @Override
-                            public void run() {
-                                mListener.onImageAvailable(FakeImageReaderProxy.this);
-                            }
-                        });
+            Runnable listenerRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    mListener.onImageAvailable(FakeImageReaderProxy.this);
+                }
+            };
+            if (mExecutor != null) {
+                mExecutor.execute(listenerRunnable);
             } else {
                 mListener.onImageAvailable(FakeImageReaderProxy.this);
             }
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 d47c9f4..1e65e192 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
@@ -24,15 +24,20 @@
 
 import static org.junit.Assume.assumeTrue;
 
+import android.content.Intent;
+
 import androidx.camera.integration.extensions.idlingresource.WaitForViewToShow;
 import androidx.camera.testing.CameraUtil;
+import androidx.test.core.app.ApplicationProvider;
 import androidx.test.espresso.Espresso;
 import androidx.test.espresso.IdlingRegistry;
 import androidx.test.espresso.IdlingResource;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.rule.GrantPermissionRule;
+import androidx.test.uiautomator.UiDevice;
 
 import org.junit.After;
 import org.junit.Before;
@@ -47,6 +52,8 @@
 @LargeTest
 public final class ToggleButtonTest {
 
+    private static final int DISMISS_LOCK_SCREEN_CODE = 82;
+
     @Rule
     public ActivityTestRule<CameraExtensionsActivity> mActivityRule =
             new ActivityTestRule<>(CameraExtensionsActivity.class);
@@ -70,6 +77,14 @@
     @Before
     public void setUp() {
         assumeTrue(CameraUtil.deviceHasCamera());
+
+        // In case the lock screen on top, the action to dismiss it.
+        UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).pressKeyCode(
+                DISMISS_LOCK_SCREEN_CODE);
+
+        // Close system dialogs first to avoid interrupt.
+        ApplicationProvider.getApplicationContext().sendBroadcast(
+                new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
     }
 
     @After
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ExtensionAvailability.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ExtensionAvailability.java
deleted file mode 100644
index 252d8ec..0000000
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ExtensionAvailability.java
+++ /dev/null
@@ -1,37 +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.camera.extensions.impl;
-
-import android.hardware.camera2.CameraCharacteristics;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * Provides abstract methods that the OEM needs to implement to check extension status.
- */
-public interface ExtensionAvailability {
-    /**
-     * Indicates whether the extension is supported on the camera device with specified camera id.
-     *
-     * @param cameraId              The camera2 id string of the camera.
-     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
-     * @return true if the extension is supported, otherwise false
-     */
-    boolean isExtensionAvailable(@NonNull String cameraId,
-            @Nullable CameraCharacteristics cameraCharacteristics);
-}
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
index 43f65c0..8bf39ae 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ImageCaptureExtenderImpl.java
@@ -23,7 +23,16 @@
 /**
  * Provides abstract methods that the OEM needs to implement to enable extensions for image capture.
  */
-public interface ImageCaptureExtenderImpl extends ExtenderStateListener, ExtensionAvailability {
+public interface ImageCaptureExtenderImpl extends ExtenderStateListener {
+    /**
+     * Indicates whether the extension is supported on the device.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     * @return true if the extension is supported, otherwise false
+     */
+    boolean isExtensionAvailable(String cameraId, CameraCharacteristics cameraCharacteristics);
+
     /**
      * Initializes the extender to be used with the specified camera.
      *
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
index 215ddc4..cfe2995 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/PreviewExtenderImpl.java
@@ -22,7 +22,7 @@
 /**
  * Provides abstract methods that the OEM needs to implement to enable extensions in the preview.
  */
-public interface PreviewExtenderImpl extends ExtenderStateListener, ExtensionAvailability {
+public interface PreviewExtenderImpl extends ExtenderStateListener {
     /** The different types of the preview processing. */
     enum ProcessorType {
         /** Processor which only updates the {@link CaptureStageImpl}. */
@@ -34,6 +34,15 @@
     }
 
     /**
+     * Indicates whether the extension is supported on the device.
+     *
+     * @param cameraId The camera2 id string of the camera.
+     * @param cameraCharacteristics The {@link CameraCharacteristics} of the camera.
+     * @return true if the extension is supported, otherwise false
+     */
+    boolean isExtensionAvailable(String cameraId, CameraCharacteristics cameraCharacteristics);
+
+    /**
      * Initializes the extender to be used with the specified camera.
      *
      * <p>This should be called before any other method on the extender. The exception is {@link
diff --git a/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/MainActivity.kt b/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/MainActivity.kt
index 63105dd..6dc6ca7 100644
--- a/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/MainActivity.kt
+++ b/camera/integration-tests/timingtestapp/src/main/java/androidx/camera/integration/antelope/MainActivity.kt
@@ -117,7 +117,7 @@
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_main)
 
-        camViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())
+        camViewModel = ViewModelProvider(this)
             .get(CamViewModel::class.java)
         cameraParams = camViewModel.getCameraParams()
         deviceInfo = DeviceInfo()
diff --git a/car/core/res/drawable/car_card_ripple_background.xml b/car/core/res/drawable/car_card_ripple_background.xml
index ca20e0f..51e3ef9 100644
--- a/car/core/res/drawable/car_card_ripple_background.xml
+++ b/car/core/res/drawable/car_card_ripple_background.xml
@@ -17,7 +17,9 @@
 <ripple
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:color="@color/car_card_ripple_background">
-    <item
-        android:id="@android:id/mask"
-        android:drawable="@android:color/white" />
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="@android:color/white" />
+        </shape>
+    </item>
 </ripple>
diff --git a/car/core/res/drawable/car_card_ripple_background_dark.xml b/car/core/res/drawable/car_card_ripple_background_dark.xml
index 880ff7a..031d495 100644
--- a/car/core/res/drawable/car_card_ripple_background_dark.xml
+++ b/car/core/res/drawable/car_card_ripple_background_dark.xml
@@ -17,7 +17,9 @@
 <ripple
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:color="@color/car_card_ripple_background_dark">
-    <item
-        android:id="@android:id/mask"
-        android:drawable="@android:color/white" />
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="@android:color/white" />
+        </shape>
+    </item>
 </ripple>
diff --git a/car/core/res/drawable/car_card_ripple_background_light.xml b/car/core/res/drawable/car_card_ripple_background_light.xml
index 5d4f2c6..a0fb41f 100644
--- a/car/core/res/drawable/car_card_ripple_background_light.xml
+++ b/car/core/res/drawable/car_card_ripple_background_light.xml
@@ -17,7 +17,9 @@
 <ripple
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:color="@color/car_card_ripple_background_light">
-    <item
-        android:id="@android:id/mask"
-        android:drawable="@android:color/white" />
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="@android:color/white" />
+        </shape>
+    </item>
 </ripple>
diff --git a/car/core/res/layout/car_alert_dialog.xml b/car/core/res/layout/car_alert_dialog.xml
index e0ae46d..bb32134 100644
--- a/car/core/res/layout/car_alert_dialog.xml
+++ b/car/core/res/layout/car_alert_dialog.xml
@@ -83,13 +83,13 @@
 
             <!-- Margins for buttons set dynamically. -->
             <Button
-                android:id="@+id/positive_button"
+                android:id="@+id/negative_button"
                 android:layout_width="wrap_content"
                 android:visibility="gone"
                 style="?attr/dialogButtonStyle" />
 
             <Button
-                android:id="@+id/negative_button"
+                android:id="@+id/positive_button"
                 android:layout_width="wrap_content"
                 android:visibility="gone"
                 style="?attr/dialogButtonStyle" />
diff --git a/car/core/res/values/styles.xml b/car/core/res/values/styles.xml
index b9d089f..ea9f5de 100644
--- a/car/core/res/values/styles.xml
+++ b/car/core/res/values/styles.xml
@@ -393,7 +393,7 @@
 
     <!-- The style for borderless buttons. -->
     <style name="Widget.Car.Button.Borderless.Colored" parent="Widget.MaterialComponents.Button.TextButton">
-        <item name="android:background">?android:attr/selectableItemBackgroundBorderless</item>
+        <item name="android:background">?android:attr/selectableItemBackground</item>
         <item name="android:fadingEdgeLength">@dimen/car_button_fading_edge_length</item>
         <item name="android:textAppearance">@style/TextAppearance.Car.Body3.Medium</item>
         <item name="android:ellipsize">none</item>
diff --git a/car/core/src/main/java/androidx/car/app/CarAlertDialog.java b/car/core/src/main/java/androidx/car/app/CarAlertDialog.java
index 9b7d908..b3110c0 100644
--- a/car/core/src/main/java/androidx/car/app/CarAlertDialog.java
+++ b/car/core/src/main/java/androidx/car/app/CarAlertDialog.java
@@ -234,13 +234,13 @@
         // If both buttons are visible, then there needs to be spacing between them.
         if ((mPositiveButton.getVisibility() == View.VISIBLE
                 && mNegativeButton.getVisibility() == View.VISIBLE)) {
-            int extraSpacingOffset = CarDialogUtil.calculateExtraButtonSpace(mPositiveButton) / 2;
-            positiveButtonLayoutParams.setMarginStart(buttonOffset - extraSpacingOffset);
-            positiveButtonLayoutParams.setMarginEnd(mButtonSpacing);
-            mPositiveButton.requestLayout();
-
-            negativeButtonLayoutParams.setMarginStart(mButtonSpacing);
+            int extraSpacingOffset = CarDialogUtil.calculateExtraButtonSpace(mNegativeButton) / 2;
+            negativeButtonLayoutParams.setMarginStart(buttonOffset - extraSpacingOffset);
+            negativeButtonLayoutParams.setMarginEnd(mButtonSpacing);
             mNegativeButton.requestLayout();
+
+            positiveButtonLayoutParams.setMarginStart(mButtonSpacing);
+            mPositiveButton.requestLayout();
         } else if (mPositiveButton.getVisibility() == View.VISIBLE) {
             int extraSpacingOffset = CarDialogUtil.calculateExtraButtonSpace(mPositiveButton) / 2;
             positiveButtonLayoutParams.setMarginStart(buttonOffset - extraSpacingOffset);
diff --git a/car/moderator/build.gradle b/car/moderator/build.gradle
index 4d19dbd..bd7370d 100644
--- a/car/moderator/build.gradle
+++ b/car/moderator/build.gradle
@@ -26,7 +26,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt
index 76f260b..adc4397 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FcsCodegenTests.kt
@@ -116,7 +116,7 @@
                 }
             """,
             { emptyMap<String, String>() },
-            "TestCall()", dumpClasses = true
+            "TestCall()"
         ).then { activity ->
             val textView = activity.findViewById<TextView>(100)
             assertEquals("12", textView.text)
@@ -1847,6 +1847,180 @@
         )
     }
 
+    @Test
+    fun testStableParameters_Various(): Unit = ensureSetup {
+        val output = ArrayList<String>()
+        compose("""
+            @Model
+            class M { var count = 0 }
+            val m = M()
+
+            @Immutable
+            data class ValueHolder(val value: Int)
+
+            var output = ArrayList<String>()
+
+            class NotStable { val value = 10 }
+
+            @Composable
+            fun MemoInt(a: Int) {
+              output.add("MemoInt a=${'$'}a")
+              Button(id=101, text="memo ${'$'}a", onClick={ m.count++ })
+            }
+
+            @Composable
+            fun MemoFloat(a: Float) {
+              output.add("MemoFloat")
+              Button(text="memo ${'$'}a")
+            }
+
+            @Composable
+            fun MemoDouble(a: Double) {
+              output.add("MemoDouble")
+              Button(text="memo ${'$'}a")
+            }
+
+            @Composable
+            fun MemoNotStable(a: NotStable) {
+              output.add("MemoNotStable")
+              Button(text="memo ${'$'}{a.value}")
+            }
+
+            @Composable
+            fun MemoModel(a: ValueHolder) {
+              output.add("MemoModelHolder")
+              Button(text="memo ${'$'}{a.value}")
+            }
+
+            @Composable
+            fun TestSkipping(
+                a: Int,
+                b: Float,
+                c: Double,
+                d: NotStable,
+                e: ValueHolder
+            ) {
+              val am = a + m.count
+              output.add("TestSkipping a=${'$'}a am=${'$'}am")
+              MemoInt(a=am)
+              MemoFloat(a=b)
+              MemoDouble(a=c)
+              MemoNotStable(a=d)
+              MemoModel(a=e)
+            }
+
+            @Composable
+            fun Main(v: ValueHolder) {
+              TestSkipping(a=1, b=1f, c=2.0, d=NotStable(), e=v)
+            }
+        """, {
+            mapOf(
+                "outerOutput: ArrayList<String>" to output
+            )
+        }, """
+            output = outerOutput
+            val v = ValueHolder(0)
+            Main(v)
+        """).then {
+            // Expect that all the methods are called in order
+            assertEquals(
+                "TestSkipping a=1 am=1, MemoInt a=1, MemoFloat, " +
+                        "MemoDouble, MemoNotStable, MemoModelHolder",
+                output.joinToString()
+            )
+            output.clear()
+        }.then { activity ->
+            // Expect TestSkipping and MemoNotStable to be called because the test forces an extra compose.
+            assertEquals("TestSkipping a=1 am=1, MemoNotStable", output.joinToString())
+            output.clear()
+
+            // Change the model
+            val button = activity.findViewById(101) as Button
+            button.performClick()
+        }.then {
+            // Expect that only MemoInt (the parameter changed) and MemoNotStable (it has unstable parameters) were
+            // called then expect a second compose which should only MemoNotStable
+            assertEquals(
+                "TestSkipping a=1 am=2, MemoInt a=2, MemoNotStable, " +
+                        "TestSkipping a=1 am=2, MemoNotStable",
+                output.joinToString()
+            )
+        }
+    }
+
+    @Test
+    fun testStableParameters_Lambdas(): Unit = ensureSetup {
+        val output = ArrayList<String>()
+        compose("""
+            @Model
+            class M { var count = 0 }
+            val m = M()
+
+            var output = ArrayList<String>()
+            val unchanged: () -> Unit = { }
+
+            fun log(msg: String) { output.add(msg) }
+
+            @Composable
+            fun Container(@Children children: () -> Unit) {
+              log("Container")
+              children()
+            }
+
+            @Composable
+            fun NormalLambda(index: Int, lambda: () -> Unit) {
+              log("NormalLambda(${'$'}index)")
+              Button(text="text")
+            }
+
+            @Composable
+            fun TestSkipping(unchanged: () -> Unit, changed: () -> Unit) {
+              log("TestSkipping")
+              Container {
+                NormalLambda(index = 1, lambda = unchanged)
+                NormalLambda(index = 2, lambda = unchanged)
+                NormalLambda(index = 3, lambda = unchanged)
+                NormalLambda(index = 4, lambda = changed)
+              }
+            }
+
+            @Composable
+            fun Main(unchanged: () -> Unit) {
+              Button(id=101, text="model ${'$'}{m.count}", onClick={ m.count++ })
+              TestSkipping(unchanged = unchanged, changed = { })
+            }
+        """, {
+            mapOf(
+                "outerOutput: ArrayList<String>" to output
+            )
+        }, """
+            output = outerOutput
+            Main(unchanged = unchanged)
+        """).then {
+            // Expect that all the methods are called in order
+            assertEquals(
+                "TestSkipping, Container, NormalLambda(1), " +
+                        "NormalLambda(2), NormalLambda(3), NormalLambda(4)",
+                output.joinToString()
+            )
+            output.clear()
+        }.then { activity ->
+            // Expect nothing to occur with no changes
+            assertEquals("", output.joinToString())
+            output.clear()
+
+            // Change the model
+            val button = activity.findViewById(101) as Button
+            button.performClick()
+        }.then {
+            // Expect only NormalLambda(4) to be called
+            assertEquals(
+                "TestSkipping, Container, NormalLambda(4)",
+                output.joinToString()
+            )
+        }
+    }
+
     override fun setUp() {
         isSetup = true
         super.setUp()
@@ -1914,7 +2088,9 @@
 
         @Suppress("NO_REFLECTION_IN_CLASS_PATH")
         val parameterList = candidateValues.map {
-            "${it.key}: ${it.value::class.qualifiedName}"
+            if (it.key.contains(':')) {
+                it.key
+            } else "${it.key}: ${it.value::class.qualifiedName}"
         }.joinToString()
         val parameterTypes = candidateValues.map {
             it.value::class.javaPrimitiveType ?: it.value::class.javaObjectType
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt
index bbea5f3..e00a888 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt
@@ -26,6 +26,7 @@
 import org.jetbrains.kotlin.name.Name
 import org.jetbrains.kotlin.resolve.annotations.argumentValue
 import org.jetbrains.kotlin.resolve.constants.ConstantValue
+import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
 import org.jetbrains.kotlin.types.KotlinType
 import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
 import org.jetbrains.kotlin.types.TypeUtils.UNIT_EXPECTED_TYPE
@@ -36,6 +37,7 @@
     val Pivotal = ComposeUtils.composeFqName("Pivotal")
     val Children = ComposeUtils.composeFqName("Children")
     val Stateful = ComposeUtils.composeFqName("Stateful")
+    val StableMarker = ComposeUtils.composeFqName("StableMarker")
     val Emittable = ComposeUtils.composeFqName("Emittable")
     val HiddenAttribute = ComposeUtils.composeFqName("HiddenAttribute")
 
@@ -59,6 +61,10 @@
 
 fun KotlinType.hasComposableAnnotation(): Boolean =
     !isSpecialType && annotations.findAnnotation(ComposeFqNames.Composable) != null
+fun KotlinType.isMarkedStable(): Boolean =
+    !isSpecialType && (
+                    annotations.hasStableMarker() ||
+                    (constructor.declarationDescriptor?.annotations?.hasStableMarker() ?: false))
 fun Annotated.hasComposableAnnotation(): Boolean =
     annotations.findAnnotation(ComposeFqNames.Composable) != null
 fun Annotated.hasPivotalAnnotation(): Boolean =
@@ -77,7 +83,7 @@
     return childrenAnnotation.isComposableChildrenAnnotation
 }
 
-private val KotlinType.isSpecialType: Boolean get() =
+internal val KotlinType.isSpecialType: Boolean get() =
     this === NO_EXPECTED_TYPE || this === UNIT_EXPECTED_TYPE
 
 val AnnotationDescriptor.isComposableAnnotation: Boolean get() = fqName == ComposeFqNames.Composable
@@ -87,4 +93,11 @@
         if (fqName != ComposeFqNames.Children) return false
         val composableValueArgument = argumentValue("composable")?.value
         return composableValueArgument == null || composableValueArgument == true
-    }
\ No newline at end of file
+    }
+
+fun Annotations.hasStableMarker(): Boolean = any(AnnotationDescriptor::isStableMarker)
+
+fun AnnotationDescriptor.isStableMarker(): Boolean {
+    val classDescriptor = annotationClass ?: return false
+    return classDescriptor.annotations.hasAnnotation(ComposeFqNames.StableMarker)
+}
\ No newline at end of file
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeSyntheticIrExtension.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeSyntheticIrExtension.kt
index 87d4766..29ba6a9 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeSyntheticIrExtension.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeSyntheticIrExtension.kt
@@ -358,9 +358,11 @@
                                 memoize.validations.map { validation ->
                                     statementGenerator.validationCall(
                                         UNDEFINED_OFFSET, UNDEFINED_OFFSET,
-                                        validation,
-                                        updaterLambdaDescriptor,
-                                        validation.assignmentLambda?.extensionReceiverParameter
+                                        memoizing = true,
+                                        validation = validation,
+                                        fnDescriptor = updaterLambdaDescriptor,
+                                        assignmentReceiver = validation.assignmentLambda
+                                            ?.extensionReceiverParameter
                                             ?: error("expected extension receiver")
                                     ) { name ->
                                         getAttribute(name)
@@ -602,6 +604,8 @@
                             ?: error("Expected callInvalidFnDescriptor to be non-null")
                         val validateParameterType =
                             getComposerCallParameterType(KtxNameConventions.CALL_INVALID_PARAMETER)
+                        val memoizing = memoize.validations.all { it.attribute.isStable }
+                        val validations = memoize.validations
                         val validateLambda =
                             lambdaExpression(
                                 validateLambdaDescriptor,
@@ -609,30 +613,51 @@
                             ) { statements ->
                             // all as one expression: a or b or c ... or z
 
-                            val validationCalls = memoize.validations
+                            val validationCalls = validations
                                 .map { validation ->
-                                    statementGenerator.validationCall(
-                                        UNDEFINED_OFFSET, UNDEFINED_OFFSET,
-                                        validation,
-                                        validateLambdaDescriptor,
-                                        validateLambdaDescriptor.valueParameters.firstOrNull()
-                                    ) { name ->
-                                        getAttribute(name)
-                                    }
+                                    if (validation.validationType == ValidationType.CHANGED &&
+                                        !memoizing)
+                                        IrConstImpl.constTrue(
+                                            UNDEFINED_OFFSET,
+                                            UNDEFINED_OFFSET,
+                                            irBuiltIns.booleanType
+                                        )
+                                    else
+                                        statementGenerator.validationCall(
+                                            UNDEFINED_OFFSET, UNDEFINED_OFFSET,
+                                            memoizing = memoizing,
+                                            validation = validation,
+                                            fnDescriptor = validateLambdaDescriptor,
+                                            assignmentReceiver = validateLambdaDescriptor
+                                                .valueParameters.firstOrNull()
+                                        ) { name ->
+                                            getAttribute(name)
+                                        }
                                 }
                             when (validationCalls.size) {
-                                0 -> Unit // TODO(lmr): return constant true here?
+                                0 -> if (!memoizing) {
+                                    // If we are not memoizing we should always return true
+                                    statements.add(IrConstImpl.constTrue(
+                                        UNDEFINED_OFFSET,
+                                        UNDEFINED_OFFSET,
+                                        irBuiltIns.booleanType
+                                    ))
+                                }
                                 1 -> statements.add(validationCalls.single())
                                 else -> {
                                     statements.add(
                                         validationCalls.reduce { left, right ->
-                                            statementGenerator.callMethod(
-                                                UNDEFINED_OFFSET, UNDEFINED_OFFSET,
-                                                resolvedKtxCall.infixOrCall
-                                                    ?: error("Invalid KTX Call"),
-                                                left
-                                            ).apply {
-                                                putValueArgument(0, right)
+                                            when {
+                                                left is IrConstImpl<*> -> right
+                                                right is IrConstImpl<*> -> left
+                                                else -> statementGenerator.callMethod(
+                                                    UNDEFINED_OFFSET, UNDEFINED_OFFSET,
+                                                    resolvedKtxCall.infixOrCall
+                                                        ?: error("Invalid KTX Call"),
+                                                    left
+                                                ).apply {
+                                                    putValueArgument(0, right)
+                                                }
                                             }
                                         }
                                     )
@@ -697,15 +722,6 @@
     }
 }
 
-private fun <T> Collection<T>.append(collection: Collection<T>): Collection<T> {
-    if (collection.isEmpty()) return this
-    if (this.isEmpty()) return collection
-    val result = arrayListOf<T>()
-    result.addAll(this)
-    result.addAll(collection)
-    return result
-}
-
 private fun StatementGenerator.getProperty(
     startOffset: Int,
     endOffset: Int,
@@ -758,13 +774,16 @@
 private fun StatementGenerator.validationCall(
     startOffset: Int,
     endOffset: Int,
+    memoizing: Boolean,
     validation: ValidatedAssignment,
     fnDescriptor: FunctionDescriptor,
     assignmentReceiver: ValueDescriptor?,
     getAttribute: (String) -> IrExpression
 ): IrCall {
-    val name = validation.attribute.name
+    val attribute = validation.attribute
+    val name = attribute.name
     val attributeValue = getAttribute(name)
+
     val validator = extensionReceiverOf(fnDescriptor)
         ?: error("expected an extension receiver to validator lambda")
 
@@ -773,10 +792,12 @@
 
     // in emit, the element is passed through an extension parameter
     // in call, the element is passed through a capture scope
-
+    val validationCall = (
+                if (memoizing) validation.validationCall
+                else validation.uncheckedValidationCall
+            ) ?: error("Expected validationCall to be non-null")
     return callMethod(
-        UNDEFINED_OFFSET, UNDEFINED_OFFSET,
-        validation.validationCall ?: error("Expected validationCall to be non-null"),
+        UNDEFINED_OFFSET, UNDEFINED_OFFSET, validationCall,
         validator
     ).apply {
         putValueArgument(0, attributeValue)
@@ -787,7 +808,7 @@
             val validationAssignment = lambdaExpression(
                 startOffset, endOffset,
                 assignmentLambdaDescriptor,
-                validation.validationCall.resultingDescriptor.valueParameters[1].type
+                validationCall.resultingDescriptor.valueParameters[1].type
             ) { statements ->
                 val parameterDefinition = validation.assignmentLambda.valueParameters.first()
                 val parameterReference = context.symbolTable.referenceValueParameter(
@@ -872,23 +893,6 @@
     }
 }
 
-private fun StatementGenerator.callFunction(
-    startOffset: Int,
-    endOffset: Int,
-    function: ResolvedCall<*>,
-    extensionReceiver: IrExpression? = null
-): IrCall {
-    val functionDescriptor = function.resultingDescriptor as FunctionDescriptor
-
-    return buildCall(
-        startOffset,
-        endOffset,
-        function,
-        functionDescriptor,
-        extensionReceiver = extensionReceiver
-    )
-}
-
 private fun StatementGenerator.callMethod(
     startOffset: Int,
     endOffset: Int,
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/KtxCallResolver.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/KtxCallResolver.kt
index 58e8157..69c6d5b 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/KtxCallResolver.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/KtxCallResolver.kt
@@ -78,6 +78,8 @@
 import androidx.compose.plugins.kotlin.analysis.ComposeDefaultErrorMessages
 import androidx.compose.plugins.kotlin.analysis.ComposeErrors
 import androidx.compose.plugins.kotlin.analysis.ComposeWritableSlices
+import androidx.compose.plugins.kotlin.analysis.ComposeWritableSlices.STABLE_TYPE
+import org.jetbrains.kotlin.builtins.KotlinBuiltIns
 import org.jetbrains.kotlin.builtins.isFunctionType
 import org.jetbrains.kotlin.resolve.BindingContext
 import org.jetbrains.kotlin.resolve.BindingTrace
@@ -140,6 +142,7 @@
 import org.jetbrains.kotlin.types.expressions.ExpressionTypingFacade
 import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo
 import org.jetbrains.kotlin.types.isError
+import org.jetbrains.kotlin.types.isNullable
 import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
 import org.jetbrains.kotlin.types.typeUtil.equalTypesOrNulls
 import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
@@ -147,9 +150,11 @@
 import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
 import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
 import org.jetbrains.kotlin.types.typeUtil.isUnit
+import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
 import org.jetbrains.kotlin.types.typeUtil.supertypes
 import org.jetbrains.kotlin.util.OperatorNameConventions
 import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
+import java.util.Locale
 import java.util.concurrent.atomic.AtomicBoolean
 
 /**
@@ -773,8 +778,16 @@
                     constantChecker
                 )
 
+                val stable = isStable(
+                    node.type,
+                    context
+                )
+
                 // update all of the nodes in the AST as "static"
-                attributeNodes[node.name]?.forEach { it.isStatic = static }
+                attributeNodes[node.name]?.forEach {
+                    it.isStatic = static
+                    it.isStable = stable
+                }
 
                 // return a node for the root of the AST that codegen can use
                 AttributeNode(
@@ -782,7 +795,8 @@
                     descriptor = node.descriptor,
                     expression = node.expression,
                     type = node.type,
-                    isStatic = static
+                    isStatic = static,
+                    isStable = stable
                 )
             }
 
@@ -1097,23 +1111,26 @@
             ExplicitReceiverKind.DISPATCH_RECEIVER -> {
                 val receiver = resolvedCall.dispatchReceiver as? ExpressionReceiver
                     ?: return emptyList()
+                val (validationCall, uncheckedValidationCall, _) = resolveValidationCall(
+                    kind = kind,
+                    validationType = ValidationType.CHANGED,
+                    attrType = receiver.type,
+                    expressionToReportErrorsOn = receiver.expression,
+                    receiverScope = receiverScope,
+                    assignmentReceiverScope = null,
+                    context = context
+                )
                 return listOf(
                     ValidatedAssignment(
                         validationType = ValidationType.CHANGED,
-                        validationCall = resolveValidationCall(
-                            kind = kind,
-                            validationType = ValidationType.CHANGED,
-                            attrType = receiver.type,
-                            expressionToReportErrorsOn = receiver.expression,
-                            receiverScope = receiverScope,
-                            assignmentReceiverScope = null,
-                            context = context
-                        ).first,
+                        validationCall = validationCall,
+                        uncheckedValidationCall = uncheckedValidationCall,
                         assignment = null,
                         assignmentLambda = null,
                         attribute = AttributeNode(
                             name = TAG_KEY,
                             isStatic = false,
+                            isStable = false,
                             type = receiver.type,
                             expression = receiver.expression,
                             descriptor = descriptor
@@ -1620,25 +1637,29 @@
             } else emptyList()
 
             val allValidations = if (!shouldMemoizeCtor) {
-                (tagValidations + setterValidations).map {
-                    when (it.validationType) {
-                        ValidationType.CHANGED -> it
+                (tagValidations + setterValidations).map { validation ->
+                    when (validation.validationType) {
+                        ValidationType.CHANGED -> validation
                         ValidationType.UPDATE,
-                        ValidationType.SET -> ValidatedAssignment(
+                        ValidationType.SET -> resolveValidationCall(
+                            kind = ComposerCallKind.CALL,
                             validationType = ValidationType.CHANGED,
-                            validationCall = resolveValidationCall(
-                                kind = ComposerCallKind.CALL,
+                            attrType = validation.attribute.type,
+                            expressionToReportErrorsOn = expression,
+                            receiverScope = invalidReceiverScope,
+                            assignmentReceiverScope = null,
+                            context = context
+                        ).let {
+                            val (validationCall, uncheckedValidationCall, _) = it
+                            ValidatedAssignment(
                                 validationType = ValidationType.CHANGED,
-                                attrType = it.attribute.type,
-                                expressionToReportErrorsOn = expression,
-                                receiverScope = invalidReceiverScope,
-                                assignmentReceiverScope = null,
-                                context = context
-                            ).first,
-                            assignment = null,
-                            attribute = it.attribute,
-                            assignmentLambda = null
-                        )
+                                validationCall = validationCall,
+                                uncheckedValidationCall = uncheckedValidationCall,
+                                assignment = null,
+                                attribute = validation.attribute,
+                                assignmentLambda = null
+                            )
+                        }
                     }
                 }
             } else tagValidations + setterValidations
@@ -1798,7 +1819,8 @@
                         descriptor = it.descriptor,
                         type = it.type,
                         expression = it.attribute.value,
-                        isStatic = false
+                        isStatic = false,
+                        isStable = false
                     )
                 }
         }
@@ -1815,7 +1837,8 @@
                     descriptor = info.descriptor,
                     type = info.type,
                     expression = attribute.value,
-                    isStatic = false
+                    isStatic = false,
+                    isStable = false
                 )
             )
         }
@@ -1835,7 +1858,8 @@
                         descriptor = descriptor,
                         type = attribute.type,
                         expression = attribute.expression,
-                        isStatic = false
+                        isStatic = false,
+                        isStable = false
                     )
                 )
                 continue
@@ -1848,7 +1872,8 @@
                         descriptor = descriptor,
                         type = attribute.type,
                         expression = attribute.expression,
-                        isStatic = false
+                        isStatic = false,
+                        isStable = false
                     )
                 )
                 continue
@@ -1900,6 +1925,7 @@
             AttributeNode(
                 name = attr.name,
                 isStatic = false,
+                isStable = false,
                 descriptor = param,
                 type = type,
                 expression = attr.value
@@ -1914,7 +1940,7 @@
         receiverScope: KotlinType,
         context: ExpressionTypingContext
     ): ValidatedAssignment {
-        val validationCall = resolveValidationCall(
+        val (validationCall, uncheckedValidationCall, _) = resolveValidationCall(
             kind = kind,
             validationType = ValidationType.CHANGED,
             attrType = type,
@@ -1922,11 +1948,12 @@
             receiverScope = receiverScope,
             assignmentReceiverScope = null,
             context = context
-        ).first
+        )
 
         return ValidatedAssignment(
             validationType = ValidationType.CHANGED,
             validationCall = validationCall,
+            uncheckedValidationCall = uncheckedValidationCall,
             attribute = this,
             assignment = null,
             assignmentLambda = null
@@ -2013,15 +2040,16 @@
                     else -> null
                 } ?: continue
 
-                val (validationCall, lambdaDescriptor) = resolveValidationCall(
-                    kind = kind,
-                    expressionToReportErrorsOn = expressionToReportErrorsOn,
-                    receiverScope = receiverScope,
-                    assignmentReceiverScope = type,
-                    validationType = validationType,
-                    attrType = attrType,
-                    context = context.replaceTraceAndCache(tempForValidations)
-                )
+                val (validationCall, uncheckedValidationCall, lambdaDescriptor) =
+                    resolveValidationCall(
+                        kind = kind,
+                        expressionToReportErrorsOn = expressionToReportErrorsOn,
+                        receiverScope = receiverScope,
+                        assignmentReceiverScope = type,
+                        validationType = validationType,
+                        attrType = attrType,
+                        context = context.replaceTraceAndCache(tempForValidations)
+                    )
 
                 results.add(
                     ValidatedAssignment(
@@ -2033,9 +2061,11 @@
                             expression = attribute.value,
                             type = attrType,
                             descriptor = resolvedCall.resultingDescriptor,
-                            isStatic = false
+                            isStatic = false,
+                            isStable = false
                         ),
-                        validationCall = validationCall
+                        validationCall = validationCall,
+                        uncheckedValidationCall = uncheckedValidationCall
                     )
                 )
                 consumedAttributes.add(name)
@@ -2114,15 +2144,16 @@
                     else -> error("Unknown callable type encountered")
                 }
 
-                val (validationCall, lambdaDescriptor) = resolveValidationCall(
-                    kind = kind,
-                    expressionToReportErrorsOn = expressionToReportErrorsOn,
-                    receiverScope = receiverScope,
-                    assignmentReceiverScope = type,
-                    validationType = validationType,
-                    attrType = attrType,
-                    context = context.replaceTraceAndCache(tempForValidations)
-                )
+                val (validationCall, uncheckedValidationCall, lambdaDescriptor) =
+                    resolveValidationCall(
+                        kind = kind,
+                        expressionToReportErrorsOn = expressionToReportErrorsOn,
+                        receiverScope = receiverScope,
+                        assignmentReceiverScope = type,
+                        validationType = validationType,
+                        attrType = attrType,
+                        context = context.replaceTraceAndCache(tempForValidations)
+                    )
 
                 results.add(
                     ValidatedAssignment(
@@ -2137,9 +2168,11 @@
                             expression = children.value,
                             type = attrType,
                             descriptor = resolvedCall.resultingDescriptor,
-                            isStatic = false
+                            isStatic = false,
+                            isStable = false
                         ),
-                        validationCall = validationCall
+                        validationCall = validationCall,
+                        uncheckedValidationCall = uncheckedValidationCall
                     )
                 )
                 consumedAttributes.add(CHILDREN_KEY)
@@ -2839,53 +2872,23 @@
         )
     }
 
-    private fun resolveValidationCall(
-        kind: ComposerCallKind,
+    private fun resolveSingleValidationCall(
         expressionToReportErrorsOn: KtExpression,
         receiverScope: KotlinType,
-        assignmentReceiverScope: KotlinType?,
         validationType: ValidationType,
+        checked: Boolean,
         attrType: KotlinType,
+        lambdaArg: ValueArgument?,
         context: ExpressionTypingContext
-    ): Pair<ResolvedCall<*>?, FunctionDescriptor?> {
-
+    ): ResolvedCall<*>? {
         val temporaryForVariable = TemporaryTraceAndCache.create(
             context, "trace to resolve variable", expressionToReportErrorsOn
         )
         val contextToUse = context.replaceTraceAndCache(temporaryForVariable)
-
-        val name = validationType.name.toLowerCase()
-        val includeLambda = validationType != ValidationType.CHANGED
-
+        val name = validationType.name.toLowerCase(Locale.ROOT).let {
+            if (!checked) (it + "Unchecked") else it
+        }
         val calleeExpression = psiFactory.createSimpleName(name)
-
-        // for call:
-        // ValidatorType.set(AttrType, (AttrType) -> Unit): Boolean
-        // ValidatorType.update(AttrType, (AttrType) -> Unit): Boolean
-        // ValidatorType.changed(AttrType): Boolean
-
-        // for emit:
-        // ValidatorType.set(AttrType, ElementType.(AttrType) -> Unit): Unit
-        // ValidatorType.update(AttrType, ElementType.(AttrType) -> Unit): Unit
-        // ValidatorType.changed(AttrType): Unit
-
-        val lambdaType = when {
-            includeLambda && kind == ComposerCallKind.EMIT -> functionType(
-                parameterTypes = listOf(attrType),
-                receiverType = assignmentReceiverScope
-            )
-            includeLambda && kind == ComposerCallKind.CALL -> functionType(
-                parameterTypes = listOf(attrType)
-            )
-            else -> null
-        }
-        val lambdaArg = lambdaType?.let { makeValueArgument(it, contextToUse) }
-        val lambdaDescriptor = lambdaType?.let {
-            createFunctionDescriptor(
-                it,
-                contextToUse
-            )
-        }
         val call = makeCall(
             callElement = calleeExpression,
             calleeExpression = calleeExpression,
@@ -2895,7 +2898,6 @@
             ),
             receiver = TransientReceiver(receiverScope)
         )
-
         val results = callResolver.resolveCallWithGivenName(
             BasicCallResolutionContext.create(
                 contextToUse,
@@ -2908,7 +2910,7 @@
             Name.identifier(name)
         )
 
-        if (results.isSuccess) return results.resultingCall to lambdaDescriptor
+        if (results.isSuccess) return results.resultingCall
 
         if (results.resultCode == OverloadResolutionResults.Code.INCOMPLETE_TYPE_INFERENCE) {
 
@@ -2967,12 +2969,79 @@
 
                 if (nextResults.isSuccess) {
                     nextTempTrace.commit()
-                    return nextResults.resultingCall to lambdaDescriptor
+                    return nextResults.resultingCall
                 }
             }
         }
 
-        return null to null
+        return null
+    }
+
+    private fun resolveValidationCall(
+        kind: ComposerCallKind,
+        expressionToReportErrorsOn: KtExpression,
+        receiverScope: KotlinType,
+        assignmentReceiverScope: KotlinType?,
+        validationType: ValidationType,
+        attrType: KotlinType,
+        context: ExpressionTypingContext
+    ): Triple<ResolvedCall<*>?, ResolvedCall<*>?, FunctionDescriptor?> {
+        val temporaryForVariable = TemporaryTraceAndCache.create(
+            context, "trace to resolve variable", expressionToReportErrorsOn
+        )
+        val contextToUse = context.replaceTraceAndCache(temporaryForVariable)
+
+        val includeLambda = validationType != ValidationType.CHANGED
+
+        // for call:
+        // ValidatorType.set(AttrType, (AttrType) -> Unit): Boolean
+        // ValidatorType.update(AttrType, (AttrType) -> Unit): Boolean
+        // ValidatorType.changed(AttrType): Boolean
+
+        // for emit:
+        // ValidatorType.set(AttrType, ElementType.(AttrType) -> Unit): Unit
+        // ValidatorType.update(AttrType, ElementType.(AttrType) -> Unit): Unit
+        // ValidatorType.changed(AttrType): Unit
+
+        val lambdaType = when {
+            includeLambda && kind == ComposerCallKind.EMIT -> functionType(
+                parameterTypes = listOf(attrType),
+                receiverType = assignmentReceiverScope
+            )
+            includeLambda && kind == ComposerCallKind.CALL -> functionType(
+                parameterTypes = listOf(attrType)
+            )
+            else -> null
+        }
+        val lambdaArg = lambdaType?.let { makeValueArgument(it, contextToUse) }
+        val lambdaDescriptor = lambdaType?.let {
+            createFunctionDescriptor(
+                it,
+                contextToUse
+            )
+        }
+
+        val validationCall = resolveSingleValidationCall(
+            expressionToReportErrorsOn = expressionToReportErrorsOn,
+            receiverScope = receiverScope,
+            validationType = validationType,
+            checked = true,
+            attrType = attrType,
+            lambdaArg = lambdaArg,
+            context = context
+        )
+
+        val uncheckedValidationCall = resolveSingleValidationCall(
+            expressionToReportErrorsOn = expressionToReportErrorsOn,
+            receiverScope = receiverScope,
+            validationType = validationType,
+            checked = false,
+            attrType = attrType,
+            lambdaArg = lambdaArg,
+            context = context
+        )
+
+        return Triple(validationCall, uncheckedValidationCall, lambdaDescriptor)
     }
 
     private fun resolveSubstitutableComposerMethod(
@@ -3719,6 +3788,22 @@
     }
 }
 
+private fun isStable(type: KotlinType?, context: ExpressionTypingContext): Boolean {
+    return type?.let {
+        val trace = context.trace
+        val calculated = trace.get(STABLE_TYPE, it)
+        if (calculated == null) {
+            val isStable = !it.isError && !it.isSpecialType && (
+                    KotlinBuiltIns.isPrimitiveType(it) ||
+                    it.isFunctionType ||
+                    it.isMarkedStable() ||
+                            (type.isNullable() && isStable(it.makeNotNullable(), context)))
+            trace.record(STABLE_TYPE, it, isStable)
+            isStable
+        } else calculated
+    } ?: false
+}
+
 private fun DeclarationDescriptor.isRoot() =
     containingDeclaration?.containingDeclaration is ModuleDescriptor
 
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 1f092e0..d5e819f 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
@@ -68,6 +68,7 @@
 class AttributeNode(
     name: String,
     var isStatic: Boolean,
+    var isStable: Boolean,
     val expression: KtExpression,
     type: KotlinType,
     descriptor: DeclarationDescriptor
@@ -97,6 +98,7 @@
 class ValidatedAssignment(
     val validationType: ValidationType,
     val validationCall: ResolvedCall<*>?,
+    val uncheckedValidationCall: ResolvedCall<*>?,
     val assignment: ResolvedCall<*>?,
     val assignmentLambda: FunctionDescriptor?,
     val attribute: AttributeNode
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeWritableSlices.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeWritableSlices.kt
index 5eadb7f..146c0b8 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeWritableSlices.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/analysis/ComposeWritableSlices.kt
@@ -1,15 +1,16 @@
 package androidx.compose.plugins.kotlin.analysis
 
+import androidx.compose.plugins.kotlin.ComposableAnnotationChecker
+import androidx.compose.plugins.kotlin.ResolvedKtxElementCall
 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
 import org.jetbrains.kotlin.descriptors.FunctionDescriptor
 import org.jetbrains.kotlin.psi.KtElement
 import org.jetbrains.kotlin.psi.KtReferenceExpression
-import androidx.compose.plugins.kotlin.ComposableAnnotationChecker
-import androidx.compose.plugins.kotlin.ResolvedKtxElementCall
 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
 import org.jetbrains.kotlin.util.slicedMap.BasicWritableSlice
 import org.jetbrains.kotlin.util.slicedMap.RewritePolicy
 import org.jetbrains.kotlin.util.slicedMap.WritableSlice
+import org.jetbrains.kotlin.types.KotlinType
 
 object ComposeWritableSlices {
     val COMPOSABLE_ANALYSIS: WritableSlice<KtElement, ComposableAnnotationChecker.Composability> =
@@ -27,6 +28,8 @@
         BasicWritableSlice(RewritePolicy.DO_NOTHING)
     val INFERRED_COMPOSABLE_DESCRIPTOR: WritableSlice<FunctionDescriptor, Boolean> =
         BasicWritableSlice(RewritePolicy.DO_NOTHING)
+    val STABLE_TYPE: WritableSlice<KotlinType, Boolean?> =
+        BasicWritableSlice(RewritePolicy.DO_NOTHING)
 }
 
 private val REWRITES_ALLOWED = object : RewritePolicy {
diff --git a/compose/compose-runtime/compose-runtime-benchmark/build.gradle b/compose/compose-runtime/compose-runtime-benchmark/build.gradle
index c82e12e..dcb32f6 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/build.gradle
+++ b/compose/compose-runtime/compose-runtime-benchmark/build.gradle
@@ -24,6 +24,7 @@
     id("com.android.library")
     id("AndroidXUiPlugin")
     id("kotlin-android")
+    id("androidx.benchmark")
 }
 
 android {
@@ -48,6 +49,7 @@
 
     androidTestImplementation(project(":compose:compose-runtime"))
     androidTestImplementation(project(":ui:ui-core"))
+    androidTestImplementation(project(":ui:ui-text"))
     androidTestImplementation(project(":ui:ui-framework"))
     androidTestImplementation(project(":ui:ui-layout"))
     androidTestImplementation(project(":ui:ui-material"))
@@ -61,7 +63,7 @@
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(KOTLIN_COMPOSE_STDLIB)
     androidTestImplementation(KOTLIN_COMPOSE_REFLECT)
-    androidTestImplementation(project(":benchmark"))
+    androidTestImplementation(project(":benchmark:benchmark-junit4"))
 }
 
 tasks.withType(KotlinCompile).configureEach {
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmark.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmark.kt
index 61625db..9d5c40f 100644
--- a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmark.kt
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmark.kt
@@ -16,29 +16,19 @@
 
 package androidx.compose.benchmark
 
-import android.widget.FrameLayout
-import androidx.benchmark.BenchmarkRule
-import androidx.benchmark.measureRepeated
 import androidx.compose.Composable
-import androidx.compose.Compose
-import androidx.compose.Composer
-import androidx.compose.FrameManager
 import androidx.compose.Model
 import androidx.compose.Observe
 import androidx.compose.benchmark.realworld4.RealWorld4_FancyWidget_000
 import androidx.compose.composer
-import androidx.compose.runWithCurrent
 import androidx.test.annotation.UiThreadTest
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
 import androidx.ui.core.dp
-import androidx.ui.core.setContent
 import androidx.ui.foundation.ColoredRect
 import androidx.ui.graphics.Color
-import org.junit.Assert.assertTrue
 import org.junit.FixMethodOrder
-import org.junit.Rule
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
@@ -46,12 +36,7 @@
 @LargeTest
 @RunWith(AndroidJUnit4::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ComposeBenchmark {
-    @get:Rule
-    val benchmarkRule = BenchmarkRule()
-
-    @get:Rule
-    val activityRule = ActivityTestRule(ComposeActivity::class.java)
+class ComposeBenchmark: ComposeBenchmarkBase() {
 
     @UiThreadTest
     @Test
@@ -76,7 +61,7 @@
     fun benchmark_03_Compose_100Rects() {
         val model = ColorModel()
         measureCompose {
-            HunderedRects(model)
+            HundredRects(model = model)
         }
     }
 
@@ -128,7 +113,7 @@
         val model = ColorModel()
         measureRecompose {
             compose {
-                HunderedRects(model, narrow = false)
+                HundredRects(model, narrow = false)
             }
             update {
                 model.toggle()
@@ -142,7 +127,7 @@
         val model = ColorModel()
         measureRecompose {
             compose {
-                HunderedRects(model, narrow = true)
+                HundredRects(model, narrow = true)
             }
             update {
                 model.toggle()
@@ -152,6 +137,7 @@
 
     @UiThreadTest
     @Test
+    @Ignore("Disabled as it appears to not do anything")
     fun benchmark_realworld4_mid_recompose() {
         val model = androidx.compose.benchmark.realworld4.createSampleData()
         measureRecompose {
@@ -164,53 +150,6 @@
         }
     }
 
-    private fun measureCompose(block: @Composable() () -> Unit) {
-        benchmarkRule.measureRepeated {
-            val root = runWithTimingDisabled {
-                val root = FrameLayout(activityRule.activity)
-                activityRule.activity.setContentView(root)
-
-                root
-            }
-
-            root.setContent {
-                block()
-            }
-
-            runWithTimingDisabled {
-                Compose.disposeComposition(root)
-            }
-        }
-    }
-
-    private fun measureRecompose(block: RecomposeReceiver.() -> Unit) {
-        val receiver = RecomposeReceiver()
-        receiver.block()
-        var activeComposer: Composer<*>? = null
-
-        val root = FrameLayout(activityRule.activity)
-        activityRule.activity.setContentView(root)
-        root.setContent {
-            activeComposer = composer.composer
-            receiver.composeCb()
-        }
-
-        benchmarkRule.measureRepeated {
-            runWithTimingDisabled {
-                receiver.updateModelCb()
-                FrameManager.nextFrame()
-            }
-
-            val didSomething = activeComposer?.let { composer ->
-                composer.runWithCurrent {
-                    composer.recompose().also { composer.applyChanges() }
-                }
-            } ?: false
-            assertTrue(didSomething)
-        }
-
-        Compose.disposeComposition(root)
-    }
 }
 
 private val color = Color.Yellow
@@ -242,7 +181,7 @@
 }
 
 @Composable
-fun HunderedRects(model: ColorModel, narrow: Boolean = false) {
+fun HundredRects(model: ColorModel, narrow: Boolean = false) {
     repeat(100) {
         if (it % 10 == 0)
             if (narrow) {
@@ -256,16 +195,3 @@
             ColoredRect(color = color, width = 10.dp, height = 10.dp)
     }
 }
-
-private class RecomposeReceiver {
-    var composeCb: @Composable() () -> Unit = @Composable { }
-    var updateModelCb: () -> Unit = { }
-
-    fun compose(block: @Composable() () -> Unit) {
-        composeCb = block
-    }
-
-    fun update(block: () -> Unit) {
-        updateModelCb = block
-    }
-}
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
new file mode 100644
index 0000000..8d72e02
--- /dev/null
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmarkBase.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.benchmark
+
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
+import androidx.compose.Composable
+import androidx.compose.Composer
+import androidx.compose.FrameManager
+import androidx.compose.composer
+import androidx.compose.disposeComposition
+import androidx.compose.runWithCurrent
+import androidx.test.rule.ActivityTestRule
+import androidx.ui.core.setContent
+import org.junit.Assert.assertTrue
+import org.junit.Rule
+
+abstract class ComposeBenchmarkBase {
+    @get:Rule
+    val benchmarkRule = BenchmarkRule()
+
+    @get:Rule
+    val activityRule = ActivityTestRule(ComposeActivity::class.java)
+
+    fun measureCompose(block: @Composable() () -> Unit) {
+        benchmarkRule.measureRepeated {
+            val activity = activityRule.activity
+
+            activity.setContent {
+                block()
+            }
+
+            runWithTimingDisabled {
+                activity.disposeComposition()
+            }
+        }
+    }
+
+    fun measureRecompose(block: RecomposeReceiver.() -> Unit) {
+        val receiver = RecomposeReceiver()
+        receiver.block()
+        var activeComposer: Composer<*>? = null
+
+        val activity = activityRule.activity
+
+        activity.setContent {
+            activeComposer = composer.composer
+            receiver.composeCb()
+        }
+
+        benchmarkRule.measureRepeated {
+            runWithTimingDisabled {
+                receiver.updateModelCb()
+                FrameManager.nextFrame()
+            }
+
+            val didSomething = activeComposer?.let { composer ->
+                composer.runWithCurrent {
+                    composer.recompose().also { composer.applyChanges() }
+                }
+            } ?: false
+            assertTrue(didSomething)
+        }
+
+        activity.disposeComposition()
+    }
+}
+
+class RecomposeReceiver {
+    var composeCb: @Composable() () -> Unit = @Composable { }
+    var updateModelCb: () -> Unit = { }
+
+    fun compose(block: @Composable() () -> Unit) {
+        composeCb = block
+    }
+
+    fun update(block: () -> Unit) {
+        updateModelCb = block
+    }
+}
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/DbMonsterBenchmark.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/DbMonsterBenchmark.kt
new file mode 100644
index 0000000..6e38b77
--- /dev/null
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/DbMonsterBenchmark.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.benchmark
+
+import android.app.Activity
+import androidx.compose.Composable
+import androidx.compose.Composer
+import androidx.compose.FrameManager
+import androidx.compose.benchmark.dbmonster.DatabaseList
+import androidx.compose.benchmark.dbmonster.DatabaseRow
+import androidx.compose.benchmark.dbmonster.Table
+import androidx.compose.composer
+import androidx.compose.disposeComposition
+import androidx.compose.runWithCurrent
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.rule.ActivityTestRule
+import androidx.ui.core.setContent
+import org.junit.Assert
+import org.junit.FixMethodOrder
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import kotlin.random.Random
+
+/**
+ * This is an implementation of a classic web perf benchmark "dbmonster". This can provide insight into apps with
+ * lots of updating parts at once. It may also be good tests for the Text and Layout stacks of compose UI.
+ *
+ * See: http://mathieuancelin.github.io/js-repaint-perfs/
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class DbMonsterBenchmark : ComposeBenchmarkBase() {
+
+    @UiThreadTest
+    @Test
+    fun dbMonster_count10_mutate10() = dbMonsterBenchmark(count = 10, mutate = 10)
+
+    @UiThreadTest
+    @Test
+    fun dbMonster_count20_mutate01() = dbMonsterBenchmark(count = 20, mutate = 1)
+
+    /**
+     * @param count - the number of databases (2x this will be number of rows)
+     * @param mutate - the number of databases to mutate/update on each frame (2x count will be 100%)
+     */
+    private fun dbMonsterBenchmark(count: Int, mutate: Int) {
+        val random = Random(0)
+        println(count)
+        println(mutate)
+        println(random)
+        val list = DatabaseList(count, random)
+        measureRecompose {
+            compose {
+                Table {
+                    for (db in list.databases) {
+                        DatabaseRow(db = db)
+                    }
+                }
+            }
+            update {
+                list.update(mutate)
+            }
+        }
+    }
+}
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/DeepTreeBenchmark.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/DeepTreeBenchmark.kt
new file mode 100644
index 0000000..89f4dd1
--- /dev/null
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/DeepTreeBenchmark.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.benchmark
+
+import androidx.compose.Composable
+import androidx.compose.benchmark.deeptree.DeepTree
+import androidx.compose.disposeComposition
+import androidx.compose.composer
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.rule.ActivityTestRule
+import androidx.ui.core.setContent
+import org.junit.FixMethodOrder
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+
+/**
+ * This is definitely a synthetic benchmark that may not map to realistic trees, but it is an effective way of
+ * stress-testing some very large and very complex trees that may map pretty well to the more complicated
+ * scenarios. I think this is a decent benchmark for testing Compose UI’s layout system in addition to
+ * Compose’s composition performance.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class DeepTreeBenchmark : ComposeBenchmarkBase() {
+    @UiThreadTest
+    @Test
+    fun benchmark_deep_tree_01_depth1_breadth100_wrap2() {
+        measureCompose {
+            DeepTree(depth = 1, breadth = 100, wrap = 2)
+        }
+    }
+
+    @UiThreadTest
+    @Test
+    fun benchmark_deep_tree_02_depth7_breadth3_wrap2() {
+        measureCompose {
+            DeepTree(depth = 7, breadth = 3, wrap = 2)
+        }
+    }
+
+    @UiThreadTest
+    @Test
+    fun benchmark_deep_tree_03_depth2_breadth10_wrap2() {
+        measureCompose {
+            DeepTree(depth = 2, breadth = 10, wrap = 2)
+        }
+    }
+
+    @UiThreadTest
+    @Test
+    fun benchmark_deep_tree_04_depth2_breadth10_wrap6() {
+        measureCompose {
+            DeepTree(depth = 2, breadth = 10, wrap = 6)
+        }
+    }
+}
\ No newline at end of file
diff --git a/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/SiblingBenchmark.kt b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/SiblingBenchmark.kt
new file mode 100644
index 0000000..dd67ae1
--- /dev/null
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/SiblingBenchmark.kt
@@ -0,0 +1,131 @@
+/*
+ * 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.benchmark
+
+import androidx.compose.Composable
+import androidx.compose.Composer
+import androidx.compose.FrameManager
+import androidx.compose.Model
+import androidx.compose.Observe
+import androidx.compose.State
+import androidx.compose.benchmark.deeptree.DeepTree
+import androidx.compose.benchmark.realworld4.RealWorld4_FancyWidget_000
+import androidx.compose.benchmark.siblings.IdentityType
+import androidx.compose.benchmark.siblings.Item
+import androidx.compose.benchmark.siblings.ReorderType
+import androidx.compose.benchmark.siblings.SiblingManagement
+import androidx.compose.benchmark.siblings.update
+import androidx.compose.composer
+import androidx.compose.disposeComposition
+import androidx.compose.runWithCurrent
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.rule.ActivityTestRule
+import androidx.ui.core.dp
+import androidx.ui.core.setContent
+import androidx.ui.foundation.ColoredRect
+import androidx.ui.graphics.Color
+import org.junit.Assert.assertTrue
+import org.junit.FixMethodOrder
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+import kotlin.random.Random
+
+/**
+ * Managing “lists” of components that are siblings in Compose and other declarative reactive frameworks ends up
+ * being an important performance characteristic to monitor. The algorithm we use here can depend greatly on how
+ * we update lists of items and we might choose to bias towards what happens more commonly in the real world,
+ * but understanding our performance characteristics for various types of list updates will be useful.
+ *
+ * @param count - number of items in the list. Really long lists probably should use a Recycler or something
+ * similar in the real world, but testing this will at least let us understand our asymptotic complexity
+ * characteristics.
+ *
+ * @param reorder - This determines what kinds of changes we will be making to the list each frame. Different list
+ * management algorithms that Compose uses will yield different trade offs depending on where items in the list
+ * are moved/added/removed/etc. For instance, we might be optimized for "append" but not "prepend", so we
+ * should benchmark these types of changes individually. Note that some like "AddMiddle" insert at a random
+ * index, so benchmarks should run this many times in order to average the randomness into something reasonable
+ * to compare with a different run of the same benchmark.
+ *
+ * @param identity - this will toggle how Compose identifies a row. These three options are slightly different and
+ * we might want to test all three.
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+class SiblingBenchmark(
+    val count: Int,
+    val reorder: ReorderType,
+    val identity: IdentityType
+) : ComposeBenchmarkBase() {
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}_{1}_{2}")
+        fun data(): Collection<Array<Any>> {
+            val counts = listOf(100)
+            val reorders = ReorderType.values()
+            val identities = IdentityType.values()
+
+            val results = mutableListOf<Array<Any>>()
+
+            for (count in counts) {
+                for (reorder in reorders) {
+                    for (identity in identities) {
+                        results.add(arrayOf(count, reorder, identity))
+                    }
+                }
+            }
+
+            return results
+        }
+    }
+
+    @UiThreadTest
+    @Test
+    fun runBenchmark() {
+        activityRule.runUiRunnable {
+            val items = ValueHolder((0..count).map { Item(it) })
+            val random = Random(0)
+            measureRecompose {
+                compose {
+                    SiblingManagement(identity = identity, items = items.value)
+                }
+                update {
+                    items.value = items.value.update(reorder, random) { Item(it + 1) }
+                }
+            }
+        }
+    }
+}
+
+// NOTE: remove when SAM conversion works in IR
+fun ActivityTestRule<ComposeActivity>.runUiRunnable(block: () -> Unit) {
+    runOnUiThread(object : Runnable {
+        override fun run() {
+            block()
+        }
+    })
+}
+
+@Model private class ValueHolder<T>(var value: T)
\ No newline at end of file
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
new file mode 100644
index 0000000..6525704
--- /dev/null
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.benchmark.dbmonster
+
+import androidx.compose.Children
+import androidx.compose.Composable
+import androidx.compose.Model
+import androidx.compose.composer
+import androidx.ui.core.Text
+import androidx.ui.layout.Column
+import androidx.ui.layout.Row
+
+import kotlin.random.Random
+
+private fun randomQuery(random: Random): String = random.nextDouble().let {
+    when {
+        it < 0.1 -> "Idle"
+        it < 0.2 -> "vacuum"
+        else -> "SELECT blah FROM something"
+    }
+}
+
+private const val MAX_ELAPSED = 15.0
+
+@Model
+class Query(random: Random, var elapsed: Double = random.nextDouble() * MAX_ELAPSED) {
+    var query = randomQuery(random)
+}
+
+@Model
+class Database(var name: String, val random: Random) {
+    var queries: List<Query> = (1..10).map {
+        Query(
+            random
+        )
+    }
+    fun topQueries(n: Int): List<Query> {
+        return queries/*.sortedByDescending { it.elapsed }*/.take(n)
+    }
+    fun update() {
+        val r = random.nextInt(queries.size)
+        (0..r).forEach {
+            queries[it].elapsed = random.nextDouble() * MAX_ELAPSED
+        }
+    }
+}
+
+class DatabaseList(n: Int, val random: Random) {
+    val databases: List<Database> = (0..n).flatMap {
+        listOf(
+            Database("cluster $it", random),
+            Database("cluster $it slave", random)
+        )
+    }
+    fun update(n: Int) {
+        // update n random databases in the list
+        databases.shuffled(random).take(n).forEach { it.update() }
+    }
+}
+
+@Composable
+fun Table(@Children children: @Composable() () -> Unit) {
+    Column { children() }
+}
+
+@Composable
+fun QueryColumn(query: Query) {
+    // TODO: we could do some conditional styling here which would make the test better
+    Column {
+        Text(text="${query.elapsed}")
+        Text(text=query.query)
+    }
+}
+
+@Composable
+fun DatabaseRow(db: Database) {
+    println(db)
+    val columns = 5
+    val topQueries = db.topQueries(columns)
+    Row {
+        Column { Text(text=db.name) }
+        Column { Text(text="${db.queries.size}") }
+        topQueries.forEach { query ->
+            QueryColumn(query = query)
+        }
+    }
+}
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
new file mode 100644
index 0000000..7712cee
--- /dev/null
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/deeptree/DeepTree.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.benchmark.deeptree
+
+import androidx.compose.Children
+import androidx.compose.Composable
+import androidx.compose.composer
+import androidx.ui.core.dp
+import androidx.ui.foundation.ColoredRect
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Column
+import androidx.ui.layout.Row
+
+@Composable
+fun Terminal(style: Int) {
+    val color = when (style) {
+        0 -> Color.Blue
+        1 -> Color.Black
+        else -> Color.Fuchsia
+    }
+    ColoredRect(color = color, height = 16.dp, width = 16.dp)
+}
+
+@Composable
+fun Stack(vertical: Boolean, @Children children: @Composable() () -> Unit) {
+    if (vertical) {
+        Column { children() }
+    } else {
+        Row { children() }
+    }
+}
+
+@Composable
+fun Container(@Children children: @Composable() () -> Unit) {
+    // non-layout node component. just adds depth to the composition hierarchy.
+    children()
+}
+
+/**
+ *
+ * This Component will emit `breadth ^ depth` Terminal components.
+ *
+ *
+ * @param depth - increasing this will determine how many nested <Stack> elements will result in the tree. Higher
+ * numbers would be a proxy for very complicated layouts
+ *
+ * @param breadth - increasing this will increase the number of nodes at each level. Correlates to exponential
+ * growth in the number of nodes in the tree, so be careful making it too large.
+ *
+ * @param wrap - to make the depth of the composition tree greater, we can increase this and it will just wrap
+ * the component this many times at each level. It will not increase the number of layout nodes in the tree, but
+ * will make composition more expensive.
+ *
+ * @param id - an int that determines the style of the next terminal
+ */
+@Suppress("UNUSED_PARAMETER")
+@Composable
+fun DeepTree(depth: Int, breadth: Int,  wrap: Int, id: Int = 0) {
+//    if (wrap > 0) {
+//        Container {
+//            DeepTree(depth=depth, breadth=breadth, wrap=wrap - 1, id=id)
+//        }
+//    } else {
+        Stack(vertical=depth % 2 == 0) {
+            if (depth == 0) {
+                Terminal(style=id % 3)
+            } else {
+                repeat(breadth) {
+                    ColoredRect(color = Color.Blue, height = 16.dp, width = 16.dp)
+//                    DeepTree(depth=depth - 1, wrap=wrap, breadth=breadth, id=id)
+                }
+            }
+        }
+//    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..6b1d4b7
--- /dev/null
+++ b/compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/siblings/SiblingManagement.kt
@@ -0,0 +1,133 @@
+/*
+ * 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.benchmark.siblings
+
+import androidx.compose.Children
+import androidx.compose.Composable
+import androidx.compose.Key
+import androidx.compose.composer
+import androidx.compose.Pivotal
+import androidx.ui.core.Text
+import androidx.ui.core.dp
+import androidx.ui.foundation.ColoredRect
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Column
+import androidx.ui.layout.Row
+import androidx.ui.text.TextStyle
+import kotlin.random.Random
+
+@Composable
+fun Stack(@Children children: @Composable() () -> Unit) {
+    Column {
+        children()
+    }
+}
+
+@Composable
+fun PivotalItemRow(@Pivotal item: Item) {
+    val color = when (item.id % 3) {
+        0 -> Color.Blue
+        1 -> Color.Black
+        else -> Color.Fuchsia
+    }
+    Row {
+        ColoredRect(color=color, width = 16.dp, height = 16.dp)
+        Text(text="${item.id}", style = TextStyle(color=color))
+    }
+}
+
+@Composable
+fun ItemRow(item: Item) {
+    // the complexity of this will influence the benchmark a lot because if
+    // identity doesn't influence what the component looks like, it's not
+    // very important to track it.
+    val color = when (item.id % 3) {
+        0 -> Color.Blue
+        1 -> Color.Black
+        else -> Color.Fuchsia
+    }
+    Row {
+        ColoredRect(color=color, width = 16.dp, height = 16.dp)
+        Text(text="${item.id}", style = TextStyle(color=color))
+    }
+}
+
+data class Item(val id: Int)
+
+enum class IdentityType { Pivotal, Index, Key }
+
+enum class ReorderType {
+    Shuffle, ShiftRight, ShiftLeft, Swap,
+    AddEnd, RemoveEnd,
+    AddStart, RemoveStart,
+    AddMiddle, RemoveMiddle
+}
+
+fun <T> List<T>.move(from: Int, to: Int): List<T> {
+    if (to < from) return move(to, from)
+    if (from == to) return this
+    val item = get(from)
+    val currentItem = get(to)
+    val left = if (from > 0) subList(0, from) else emptyList()
+    val right = if (to < size) subList(to + 1, size) else emptyList()
+    val middle = if (to - from > 1) subList(from + 1, to) else emptyList()
+    return left + listOf(currentItem) + middle + listOf(item) + right
+}
+
+fun <T> List<T>.update(reorderType: ReorderType, random: Random, factory: (Int) -> T): List<T> {
+    // NOTE: might be some off by one errors in here :)
+    @Suppress("ReplaceSingleLineLet")
+    return when (reorderType) {
+        ReorderType.Shuffle -> shuffled(random)
+        ReorderType.ShiftRight -> listOf(get(size - 1)) + subList(0, size - 1)
+        ReorderType.ShiftLeft -> subList(1, size) + listOf(get(0))
+        ReorderType.Swap -> move(random.nextInt(size), random.nextInt(size))
+        ReorderType.AddEnd -> this + listOf(factory(size))
+        ReorderType.RemoveEnd -> dropLast(1)
+        ReorderType.AddStart -> listOf(factory(size)) + this
+        ReorderType.RemoveStart -> drop(1)
+        ReorderType.AddMiddle -> random.nextInt(size).let {
+            subList(0, it) + listOf(factory(size)) + subList(it, size)
+        }
+        ReorderType.RemoveMiddle -> random.nextInt(size).let { filterIndexed { i, _ -> i == it } }
+    }
+}
+
+@Composable
+fun SiblingManagement(identity: IdentityType, items: List<Item>) {
+    Stack {
+        when (identity) {
+            IdentityType.Pivotal -> {
+                for (item in items) {
+                    PivotalItemRow(item=item)
+                }
+            }
+            IdentityType.Index -> {
+                for (item in items) {
+                    ItemRow(item=item)
+                }
+            }
+            IdentityType.Key -> {
+                for (item in items) {
+                    Key(key=item.id) {
+                        ItemRow(item=item)
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Applier.kt b/compose/compose-runtime/src/main/java/androidx/compose/Applier.kt
index a2090a3..f94b0a5 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Applier.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/Applier.kt
@@ -16,8 +16,6 @@
 
 package androidx.compose
 
-import java.util.Stack
-
 /**
  * An adapter that performs tree based operations on some tree startNode N without requiring a specific base type for N
  */
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 b5b4175..e5fb5ec 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Composer.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/Composer.kt
@@ -16,8 +16,6 @@
 
 package androidx.compose
 
-import java.util.Stack
-
 internal typealias Change<N> = (
     applier: Applier<N>,
     slots: SlotWriter,
@@ -474,8 +472,8 @@
         if (insertedProviders.isNotEmpty()) {
             var current = insertedProviders.size - 1
             while (current >= 0) {
-                val element = insertedProviders[current]
-                if (element is Ambient.Holder<*> && element.ambient === key) {
+                val element = insertedProviders.peek(current)
+                if (element.ambient === key) {
                     @Suppress("UNCHECKED_CAST")
                     return element.value as? T ?: key.defaultValue
                 }
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Immutability.kt b/compose/compose-runtime/src/main/java/androidx/compose/Immutability.kt
deleted file mode 100644
index fb5689e..0000000
--- a/compose/compose-runtime/src/main/java/androidx/compose/Immutability.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose
-
-/**
- * Just a dummy implementation to prove the behavior for a couple simple cases.
- * TODO: Should return true for deeply immutable objects, frozen objects, primitives, value types, inline classes of immutables, @Model
- * TODO: When we know at compile time, we shouldn't be doing a runtime check for this
- */
-@PublishedApi
-internal fun isEffectivelyImmutable(value: Any?): Boolean {
-    return when (value) {
-        is String,
-        is Int,
-        is Double,
-        is Float,
-        is Short,
-        is Byte,
-        is Char,
-        is Boolean -> true
-        else -> false
-    }
-}
\ No newline at end of file
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Immutable.kt b/compose/compose-runtime/src/main/java/androidx/compose/Immutable.kt
new file mode 100644
index 0000000..a490564
--- /dev/null
+++ b/compose/compose-runtime/src/main/java/androidx/compose/Immutable.kt
@@ -0,0 +1,23 @@
+/*
+ * 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
+
+@MustBeDocumented
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.BINARY)
+@StableMarker
+annotation class Immutable
\ No newline at end of file
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 5557f00..00c0414 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: () -> Unit) {
+fun Key(@Suppress("UNUSED_PARAMETER") @Pivotal key: Any?, @Children children: @Composable() () -> Unit) {
     children()
 }
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/Model.kt b/compose/compose-runtime/src/main/java/androidx/compose/Model.kt
index 6a53af3..9682b69 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/Model.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/Model.kt
@@ -53,4 +53,5 @@
 @MustBeDocumented
 @Target(AnnotationTarget.CLASS)
 @Retention(AnnotationRetention.BINARY)
+@StableMarker
 annotation class Model
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/StableMarker.kt b/compose/compose-runtime/src/main/java/androidx/compose/StableMarker.kt
new file mode 100644
index 0000000..1f8111d
--- /dev/null
+++ b/compose/compose-runtime/src/main/java/androidx/compose/StableMarker.kt
@@ -0,0 +1,33 @@
+/*
+ * 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
+
+/**
+ * [StableMarker] marks an annotation as indicating a type as having a stable
+ * equals comparision that can be used during composition. When all types passed
+ * as parameters to a [Composable] function are marked as stable then then the
+ * parameter values are compared for equality based on positional memoization and
+ * the call is skipped if all the values are the equal to the previous call.
+ *
+ * Primitive value types (such as Int, Float, etc), String and enum types are
+ * considered, a priori, stable.
+*
+ */
+@MustBeDocumented
+@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.BINARY)
+annotation class StableMarker
\ No newline at end of file
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/IntStack.kt b/compose/compose-runtime/src/main/java/androidx/compose/Stack.kt
similarity index 73%
rename from compose/compose-runtime/src/main/java/androidx/compose/IntStack.kt
rename to compose/compose-runtime/src/main/java/androidx/compose/Stack.kt
index 340c424..b586297 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/IntStack.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/Stack.kt
@@ -16,6 +16,20 @@
 
 package androidx.compose
 
+internal class Stack<T> {
+    private val backing = ArrayList<T>()
+
+    val size: Int get() = backing.size
+
+    fun push(value: T) = backing.add(value)
+    fun pop(): T = backing.removeAt(size - 1)
+    fun peek(): T = backing.get(size - 1)
+    fun peek(index: Int): T = backing.get(index)
+    fun isEmpty() = backing.isEmpty()
+    fun isNotEmpty() = !isEmpty()
+    fun clear() = backing.clear()
+}
+
 internal class IntStack {
     private var slots = IntArray(10)
     private var tos = 0
@@ -35,4 +49,4 @@
     fun isEmpty() = tos == 0
     fun isNotEmpty() = tos != 0
     fun clear() { tos = 0 }
-}
\ No newline at end of file
+}
diff --git a/compose/compose-runtime/src/main/java/androidx/compose/ViewComposer.kt b/compose/compose-runtime/src/main/java/androidx/compose/ViewComposer.kt
index 8b6d1f1..90ba367 100644
--- a/compose/compose-runtime/src/main/java/androidx/compose/ViewComposer.kt
+++ b/compose/compose-runtime/src/main/java/androidx/compose/ViewComposer.kt
@@ -20,7 +20,6 @@
 import android.view.View
 import android.view.ViewGroup
 import androidx.compose.adapters.getViewAdapterIfExists
-import java.util.Stack
 
 class ViewAdapters {
     private val adapters = mutableListOf<(parent: Any, child: Any) -> Any?>()
@@ -269,7 +268,17 @@
     // TODO: Add more overloads for common primitive types like String and Float etc to avoid boxing
     // and the immutable check
     @Suppress("NOTHING_TO_INLINE")
-    inline fun changed(value: Int) = with(composer) {
+    fun changed(value: Int) = with(composer) {
+        if ((nextSlot() as? Int)?.let { value != it } ?: true || inserting) {
+            updateValue(value)
+            true
+        } else {
+            skipValue()
+            false
+        }
+    }
+
+    fun <T> changed(value: T) = with(composer) {
         if (nextSlot() != value || inserting) {
             updateValue(value)
             true
@@ -279,20 +288,10 @@
         }
     }
 
-    inline fun <reified T> changed(value: T) = with(composer) {
-        if (nextSlot() != value || inserting || !isEffectivelyImmutable(value)) {
-            updateValue(value)
-            true
-        } else {
-            skipValue()
-            false
-        }
-    }
-
     @Suppress("NOTHING_TO_INLINE")
-    inline fun updated(value: Int) = with(composer) {
+    fun updated(value: Int) = with(composer) {
         inserting.let { inserting ->
-            if (nextSlot() != value || inserting) {
+            if (((nextSlot() as? Int)?.let { it != value } ?: true) || inserting) {
                 updateValue(value)
                 !inserting
             } else {
@@ -302,9 +301,9 @@
         }
     }
 
-    inline fun <reified T> updated(value: T) = with(composer) {
+    fun <T> updated(value: T) = with(composer) {
         inserting.let { inserting ->
-            if (nextSlot() != value || inserting || !isEffectivelyImmutable(value)) {
+            if (nextSlot() != value || inserting) {
                 updateValue(value)
                 !inserting
             } else {
@@ -326,6 +325,21 @@
     inline fun <reified T> update(value: T, /*crossinline*/ block: (value: T) -> Unit): Boolean =
         updated(value).also { if (it) block(value) }
 
+    @Suppress("UNUSED")
+    fun <T> changedUnchecked(@Suppress("UNUSED_PARAMETER") value: T) = true
+
+    @Suppress("UNUSED")
+    inline fun <T> setUnchecked(value: T, block: (value: T) -> Unit): Boolean {
+        block(value)
+        return true
+    }
+
+    @Suppress("UNUSED")
+    inline fun <T> updateUnchecked(value: T, block: (value: T) -> Unit): Boolean {
+        block(value)
+        return true
+    }
+
     /*inline*/ operator fun Boolean.plus(other: Boolean) = this || other
 }
 
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 d19d59a..6a58b69 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/CompositionTests.kt
+++ b/compose/compose-runtime/src/test/java/androidx/compose/CompositionTests.kt
@@ -706,6 +706,94 @@
         }
     }
 
+    fun testInvalidationAfterRemoval() {
+        val slReportReports = object {}
+        var recomposeLois: (() -> Unit)? = null
+        val key = 0
+
+        class Reporter : ViewComponent() {
+            var report: Report? = null
+
+            override fun compose() {
+                val r = report
+                if (r != null) {
+                    if (r.from == "Lois" || r.to == "Lois") recomposeLois = { recompose() }
+                    cc.startGroup(key)
+                    text(r.from)
+                    text("reports to")
+                    text(r.to)
+                    cc.endGroup()
+                } else {
+                    text("no report to report")
+                }
+            }
+        }
+
+        fun MockViewComposition.reportsReport(
+            reports: Iterable<Report>,
+            include: (report: Report) -> Boolean
+        ) {
+            linear {
+                repeat(of = reports) { report ->
+                    if (include(report)) {
+                        call(
+                            slReportReports,
+                            { Reporter() },
+                            { set(report) { this.report = it } },
+                            { it() }
+                        )
+                    }
+                }
+            }
+        }
+
+        val r = Report("Lois", "Perry")
+        val reports = listOf(
+            jim_reports_to_sally,
+            rob_reports_to_alice,
+            clark_reports_to_lois,
+            r
+        )
+        val all: (report: Report) -> Boolean = { true }
+        val notLois: (report: Report) -> Boolean = { it.from != "Lois" && it.to != "Lois" }
+        val composer = compose {
+            reportsReport(reports, all)
+        }.apply { applyChanges() }
+
+        validate(composer.root) {
+            linear {
+                reportsTo(jim_reports_to_sally)
+                reportsTo(rob_reports_to_alice)
+                reportsTo(clark_reports_to_lois)
+                reportsTo(r)
+            }
+        }
+
+        compose(composer, expectChanges = true) {
+            reportsReport(reports, notLois)
+        }
+
+        validate(composer.root) {
+            linear {
+                reportsTo(jim_reports_to_sally)
+                reportsTo(rob_reports_to_alice)
+            }
+        }
+
+        // Invalidate Lois which is now removed.
+        recomposeLois?.let { it() }
+
+        composer.recompose()
+        composer.applyChanges()
+
+        validate(composer.root) {
+            linear {
+                reportsTo(jim_reports_to_sally)
+                reportsTo(rob_reports_to_alice)
+            }
+        }
+    }
+
     // remember()
 
     fun testSimpleRemember() {
diff --git a/compose/compose-runtime/src/test/java/androidx/compose/ViewComposerTests.kt b/compose/compose-runtime/src/test/java/androidx/compose/ViewComposerTests.kt
index 8f3ebb8b..9acdbcc 100644
--- a/compose/compose-runtime/src/test/java/androidx/compose/ViewComposerTests.kt
+++ b/compose/compose-runtime/src/test/java/androidx/compose/ViewComposerTests.kt
@@ -234,7 +234,7 @@
             //  @Composable
             //  fun PhoneView(phone: Phone) {
             //    phoneCalled++
-            //   <TextView text="..." />
+            //   TextView(text="...")
             //  }
             fun PhoneView(phone: Phone) {
                 phoneCalled++
@@ -253,11 +253,11 @@
         }.then { _ ->
             assertEquals(1, phoneCalled)
         }.then { _ ->
-            assertEquals(2, phoneCalled)
+            assertEquals(1, phoneCalled)
 
             phone = Phone("124", "456", "7890")
         }.then { _ ->
-            assertEquals(3, phoneCalled)
+            assertEquals(2, phoneCalled)
         }
     }
 
diff --git a/concurrent/futures/api/1.0.0-rc01.txt b/concurrent/futures/api/1.0.0-rc01.txt
new file mode 100644
index 0000000..beb76bd
--- /dev/null
+++ b/concurrent/futures/api/1.0.0-rc01.txt
@@ -0,0 +1,21 @@
+// Signature format: 3.0
+package androidx.concurrent.futures {
+
+  public final class CallbackToFutureAdapter {
+    method public static <T> com.google.common.util.concurrent.ListenableFuture<T!> getFuture(androidx.concurrent.futures.CallbackToFutureAdapter.Resolver<T!>);
+  }
+
+  public static final class CallbackToFutureAdapter.Completer<T> {
+    method public void addCancellationListener(Runnable, java.util.concurrent.Executor);
+    method protected void finalize();
+    method public boolean set(T!);
+    method public boolean setCancelled();
+    method public boolean setException(Throwable);
+  }
+
+  public static interface CallbackToFutureAdapter.Resolver<T> {
+    method public Object? attachCompleter(androidx.concurrent.futures.CallbackToFutureAdapter.Completer<T!>) throws java.lang.Exception;
+  }
+
+}
+
diff --git a/concurrent/futures/api/restricted_1.0.0-rc01.txt b/concurrent/futures/api/restricted_1.0.0-rc01.txt
new file mode 100644
index 0000000..6dabf0b
--- /dev/null
+++ b/concurrent/futures/api/restricted_1.0.0-rc01.txt
@@ -0,0 +1,45 @@
+// Signature format: 3.0
+package androidx.concurrent.futures {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class AbstractResolvableFuture<V> implements com.google.common.util.concurrent.ListenableFuture<V> {
+    ctor protected AbstractResolvableFuture();
+    method public final void addListener(Runnable!, java.util.concurrent.Executor!);
+    method protected void afterDone();
+    method public final boolean cancel(boolean);
+    method public final V! get(long, java.util.concurrent.TimeUnit!) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+    method public final V! get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
+    method protected void interruptTask();
+    method public final boolean isCancelled();
+    method public final boolean isDone();
+    method protected String? pendingToString();
+    method protected boolean set(V?);
+    method protected boolean setException(Throwable!);
+    method protected boolean setFuture(com.google.common.util.concurrent.ListenableFuture<? extends V>!);
+    method protected final boolean wasInterrupted();
+  }
+
+  public final class CallbackToFutureAdapter {
+    method public static <T> com.google.common.util.concurrent.ListenableFuture<T!> getFuture(androidx.concurrent.futures.CallbackToFutureAdapter.Resolver<T!>);
+  }
+
+  public static final class CallbackToFutureAdapter.Completer<T> {
+    method public void addCancellationListener(Runnable, java.util.concurrent.Executor);
+    method protected void finalize();
+    method public boolean set(T!);
+    method public boolean setCancelled();
+    method public boolean setException(Throwable);
+  }
+
+  public static interface CallbackToFutureAdapter.Resolver<T> {
+    method public Object? attachCompleter(androidx.concurrent.futures.CallbackToFutureAdapter.Completer<T!>) throws java.lang.Exception;
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class ResolvableFuture<V> extends androidx.concurrent.futures.AbstractResolvableFuture<V> {
+    method public static <V> androidx.concurrent.futures.ResolvableFuture<V!>! create();
+    method public boolean set(V?);
+    method public boolean setException(Throwable!);
+    method public boolean setFuture(com.google.common.util.concurrent.ListenableFuture<? extends V>!);
+  }
+
+}
+
diff --git a/content/build.gradle b/content/build.gradle
index cafb452..4a27186 100644
--- a/content/build.gradle
+++ b/content/build.gradle
@@ -26,7 +26,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.1.0")
 
     androidTestImplementation(JUNIT)
diff --git a/coordinatorlayout/build.gradle b/coordinatorlayout/build.gradle
index ae2a733..6050e84 100644
--- a/coordinatorlayout/build.gradle
+++ b/coordinatorlayout/build.gradle
@@ -11,7 +11,7 @@
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
     // TODO: change to 1.1.0-alpha04 after release
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.customview:customview:1.0.0")
 
diff --git a/core/core/api/restricted_1.2.0-alpha03.txt b/core/core/api/restricted_1.2.0-alpha03.txt
index 0693bb2..138fb9f 100644
--- a/core/core/api/restricted_1.2.0-alpha03.txt
+++ b/core/core/api/restricted_1.2.0-alpha03.txt
@@ -140,13 +140,13 @@
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ComponentActivity extends android.app.Activity implements androidx.core.view.KeyEventDispatcher.Component {
     ctor public ComponentActivity();
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T extends androidx.core.app.ComponentActivity.ExtraData> T! getExtraData(Class<T!>!);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void putExtraData(androidx.core.app.ComponentActivity.ExtraData!);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T extends androidx.core.app.ComponentActivity.ExtraData> T! getExtraData(Class<T!>!);
+    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!);
   }
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class ComponentActivity.ExtraData {
-    ctor public ComponentActivity.ExtraData();
+  @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class ComponentActivity.ExtraData {
+    ctor @Deprecated public ComponentActivity.ExtraData();
   }
 
   @RequiresApi(api=28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class CoreComponentFactory extends android.app.AppComponentFactory {
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 0693bb2..138fb9f 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -140,13 +140,13 @@
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ComponentActivity extends android.app.Activity implements androidx.core.view.KeyEventDispatcher.Component {
     ctor public ComponentActivity();
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T extends androidx.core.app.ComponentActivity.ExtraData> T! getExtraData(Class<T!>!);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void putExtraData(androidx.core.app.ComponentActivity.ExtraData!);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T extends androidx.core.app.ComponentActivity.ExtraData> T! getExtraData(Class<T!>!);
+    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!);
   }
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class ComponentActivity.ExtraData {
-    ctor public ComponentActivity.ExtraData();
+  @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class ComponentActivity.ExtraData {
+    ctor @Deprecated public ComponentActivity.ExtraData();
   }
 
   @RequiresApi(api=28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class CoreComponentFactory extends android.app.AppComponentFactory {
diff --git a/core/core/src/androidTest/java/androidx/core/app/ComponentActivityTest.java b/core/core/src/androidTest/java/androidx/core/app/ComponentActivityTest.java
index cda201b..98f553f 100644
--- a/core/core/src/androidTest/java/androidx/core/app/ComponentActivityTest.java
+++ b/core/core/src/androidTest/java/androidx/core/app/ComponentActivityTest.java
@@ -27,6 +27,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+@SuppressWarnings("deprecation")
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ComponentActivityTest extends BaseInstrumentationTestCase<TestComponentActivity> {
@@ -54,10 +55,10 @@
         assertEquals(mTestExtraData, mComponentActivity.getExtraData(TestExtraData.class));
     }
 
-    public class NeverAddedExtraData extends ComponentActivity.ExtraData {
+    private class NeverAddedExtraData extends ComponentActivity.ExtraData {
     }
 
-    public class TestExtraData extends ComponentActivity.ExtraData {
+    private class TestExtraData extends ComponentActivity.ExtraData {
     }
 }
 
diff --git a/core/core/src/androidTest/java/androidx/core/os/TraceCompatTest.java b/core/core/src/androidTest/java/androidx/core/os/TraceCompatTest.java
index d36b79a..5f229c4 100644
--- a/core/core/src/androidTest/java/androidx/core/os/TraceCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/os/TraceCompatTest.java
@@ -21,15 +21,18 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import android.app.UiAutomation;
+import android.os.Build;
 import android.os.ParcelFileDescriptor;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -50,12 +53,23 @@
         mByteArrayOutputStream = new ByteArrayOutputStream();
     }
 
+    @After
+    public void stopAtrace() throws IOException {
+        // Since API 23, 'async_stop' will work. On lower API levels it was broken (see aosp/157142)
+        if (Build.VERSION.SDK_INT >= 23) {
+            executeCommand("atrace --async_stop");
+        } else {
+            // Ensure tracing is not currently running by performing a short synchronous trace.
+            executeCommand("atrace -t 0");
+        }
+    }
+
     @Test
     public void beginAndEndSection() throws IOException {
         startTrace();
         TraceCompat.beginSection("beginAndEndSection");
         TraceCompat.endSection();
-        endTrace();
+        dumpTrace();
 
         assertTraceContains("tracing_mark_write:\\ B\\|.*\\|beginAndEndSection");
         assertTraceContains("tracing_mark_write:\\ E");
@@ -66,7 +80,7 @@
         startTrace();
         TraceCompat.beginAsyncSection("beginAndEndSectionAsync", /*cookie=*/5099);
         TraceCompat.endAsyncSection("beginAndEndSectionAsync", /*cookie=*/5099);
-        endTrace();
+        dumpTrace();
 
         assertTraceContains("tracing_mark_write:\\ S\\|.*\\|beginAndEndSectionAsync\\|5099");
         assertTraceContains("tracing_mark_write:\\ F\\|.*\\|beginAndEndSectionAsync\\|5099");
@@ -78,7 +92,7 @@
         TraceCompat.setCounter("counterName", 42);
         TraceCompat.setCounter("counterName", 47);
         TraceCompat.setCounter("counterName", 9787);
-        endTrace();
+        dumpTrace();
 
         assertTraceContains("tracing_mark_write:\\ C\\|.*\\|counterName\\|42");
         assertTraceContains("tracing_mark_write:\\ C\\|.*\\|counterName\\|47");
@@ -89,7 +103,7 @@
     public void isEnabledDuringTrace() throws IOException {
         startTrace();
         boolean enabled = TraceCompat.isEnabled();
-        endTrace();
+        dumpTrace();
 
         assertThat(enabled).isTrue();
     }
@@ -101,39 +115,45 @@
     }
 
     private void startTrace() throws IOException {
-        UiAutomation automation = InstrumentationRegistry.getInstrumentation()
-                .getUiAutomation();
         String processName =
                 ApplicationProvider.getApplicationContext().getApplicationInfo().processName;
 
         // Write the "async_start" status to the byte array to ensure atrace has fully started
         // before issuing any trace commands. This will also capture any errors that occur during
         // start so they can be added to the assertion error's message.
-        writeDataToByteStream(automation.executeShellCommand(
-                String.format("atrace --async_start -b %d -a %s", TRACE_BUFFER_SIZE,
-                        processName)),
+        executeCommand(
+                String.format("atrace --async_start -b %d -a %s", TRACE_BUFFER_SIZE, processName));
+    }
+
+    private void dumpTrace() throws IOException {
+        // On older versions of atrace, the -b option is required when dumping the trace so the
+        // trace buffer doesn't get cleared before being dumped.
+        executeCommand(
+                String.format("atrace --async_dump -b %d", TRACE_BUFFER_SIZE),
                 mByteArrayOutputStream);
     }
 
-    private void endTrace() throws IOException {
+    private static void executeCommand(@NonNull String command) throws IOException {
+        executeCommand(command, null);
+    }
+
+    private static void executeCommand(@NonNull String command,
+            @Nullable ByteArrayOutputStream outputStream) throws IOException {
         UiAutomation automation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
-        writeDataToByteStream(automation.executeShellCommand("atrace --async_stop"),
-                mByteArrayOutputStream);
-    }
 
-    private void writeDataToByteStream(ParcelFileDescriptor pfDescriptor,
-            ByteArrayOutputStream outputStream) throws IOException {
-        try (ParcelFileDescriptor.AutoCloseInputStream inputStream =
+        try (ParcelFileDescriptor pfDescriptor = automation.executeShellCommand(command);
+             ParcelFileDescriptor.AutoCloseInputStream inputStream =
                      new ParcelFileDescriptor.AutoCloseInputStream(
                              pfDescriptor)) {
             byte[] buffer = new byte[1024];
 
             int length;
             while ((length = inputStream.read(buffer)) >= 0) {
-                outputStream.write(buffer, 0, length);
+                if (outputStream != null) {
+                    outputStream.write(buffer, 0, length);
+                }
             }
         }
-
     }
 
     private void assertTraceContains(@NonNull String contentRegex) {
diff --git a/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewNestedScrollingA11yScrollTest.java b/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewNestedScrollingA11yScrollTest.java
new file mode 100644
index 0000000..af6e3bc
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewNestedScrollingA11yScrollTest.java
@@ -0,0 +1,336 @@
+/*
+ * 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.core.widget;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.graphics.drawable.GradientDrawable;
+import android.support.v4.BaseInstrumentationTestCase;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.test.R;
+import androidx.core.view.NestedScrollingParent3;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+
+import org.jetbrains.annotations.NotNull;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Large integration test that verifies that NestedScrollView participates in nested scrolling when
+ * scrolling occurs due to a11y actions.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class NestedScrollViewNestedScrollingA11yScrollTest extends
+        BaseInstrumentationTestCase<TestContentViewActivity> {
+
+    private static final int CHILD_HEIGHT = 300;
+    private static final int NSV_HEIGHT = 100;
+    private static final int PARENT_HEIGHT = 300;
+    private static final int WIDTH = 400;
+    // A11Y scroll only scrolls the height of the NestedScrollView at max.
+    private static final int TOTAL_SCROLL_OFFSET = 100;
+
+    private NestedScrollView mNestedScrollView;
+    private NestedScrollingSpyView mParent;
+
+    public NestedScrollViewNestedScrollingA11yScrollTest() {
+        super(TestContentViewActivity.class);
+    }
+
+    @Before
+    public void setup() throws Throwable {
+        Context context = mActivityTestRule.getActivity();
+
+        View child = new View(context);
+        child.setMinimumWidth(WIDTH);
+        child.setMinimumHeight(CHILD_HEIGHT);
+        child.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, CHILD_HEIGHT));
+        child.setBackgroundDrawable(
+                new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM,
+                        new int[]{0xFFFF0000, 0xFF00FF00}));
+
+        mNestedScrollView = new NestedScrollView(context);
+        mNestedScrollView.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, NSV_HEIGHT));
+        mNestedScrollView.setBackgroundColor(0xFF0000FF);
+        mNestedScrollView.addView(child);
+
+        mParent = spy(new NestedScrollingSpyView(context));
+        mParent.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, PARENT_HEIGHT));
+        mParent.setBackgroundColor(0xFF0000FF);
+        mParent.addView(mNestedScrollView);
+
+        // Attach to activity and wait for layouts.
+        final TestContentView testContentView =
+                mActivityTestRule.getActivity().findViewById(R.id.testContentView);
+        testContentView.expectLayouts(1);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                testContentView.addView(mParent);
+            }
+        });
+        testContentView.awaitLayouts(2);
+    }
+
+    @Test
+    public void a11yActionScrollForward_fullyParticipatesInNestedScrolling() throws Throwable {
+        a11yScroll_fullyParticipatesInNestedScrolling(true);
+    }
+
+    @Test
+    public void a11yActionScrollBackward_fullyParticipatesInNestedScrolling() throws Throwable {
+        a11yScroll_fullyParticipatesInNestedScrolling(false);
+    }
+
+    private void a11yScroll_fullyParticipatesInNestedScrolling(final boolean forward)
+            throws Throwable {
+
+        final CountDownLatch countDownLatch = new CountDownLatch(2);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                doReturn(true).when(mParent).onStartNestedScroll(any(View.class), any(View.class),
+                        anyInt(), anyInt());
+
+                int action;
+                if (forward) {
+                    action = AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD;
+                } else {
+                    mNestedScrollView.scrollTo(0, 200);
+                    action = AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD;
+                }
+
+                mParent.mOnStopNestedScrollListener =
+                        new NestedScrollingSpyView.OnStopNestedScrollListener() {
+                            @Override
+                            public void onStopNestedScroll(int type) {
+                                if (type == ViewCompat.TYPE_NON_TOUCH) {
+                                    countDownLatch.countDown();
+                                }
+                            }
+                        };
+
+                mNestedScrollView.setOnScrollChangeListener(
+                        new NestedScrollView.OnScrollChangeListener() {
+                            @Override
+                            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY,
+                                    int oldScrollX, int oldScrollY) {
+                                if (scrollY == TOTAL_SCROLL_OFFSET) {
+                                    countDownLatch.countDown();
+                                }
+                            }
+                        });
+
+                ViewCompat.performAccessibilityAction(mNestedScrollView, action, null);
+            }
+        });
+        assertThat(countDownLatch.await(2, TimeUnit.SECONDS), is(true));
+
+        // Verify that none of the following TYPE_TOUCH nested scrolling methods are called.
+        verify(mParent, never()).onStartNestedScroll(mNestedScrollView, mNestedScrollView,
+                ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
+        verify(mParent, never()).onNestedScrollAccepted(mNestedScrollView, mNestedScrollView,
+                ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
+        verify(mParent, never()).onNestedPreScroll(eq(mNestedScrollView), anyInt(), anyInt(),
+                any(int[].class), eq(ViewCompat.TYPE_TOUCH));
+        verify(mParent, never()).onNestedScroll(eq(mNestedScrollView), anyInt(), anyInt(),
+                anyInt(), anyInt(), eq(ViewCompat.TYPE_TOUCH), any(int[].class));
+        verify(mParent, never()).onNestedPreFling(eq(mNestedScrollView), anyFloat(),
+                anyFloat());
+        verify(mParent, never()).onNestedFling(eq(mNestedScrollView), anyFloat(), anyFloat(),
+                eq(true));
+        verify(mParent, never()).onStopNestedScroll(mNestedScrollView, ViewCompat.TYPE_TOUCH);
+
+        // Verify all of the following TYPE_NON_TOUCH nested scrolling methods are called
+        verify(mParent, atLeastOnce()).onStartNestedScroll(mNestedScrollView, mNestedScrollView,
+                ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);
+        verify(mParent, atLeastOnce()).onNestedScrollAccepted(mNestedScrollView, mNestedScrollView,
+                ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);
+        verify(mParent, atLeastOnce()).onNestedPreScroll(eq(mNestedScrollView), anyInt(), anyInt(),
+                any(int[].class), eq(ViewCompat.TYPE_NON_TOUCH));
+        verify(mParent, atLeastOnce()).onNestedScroll(eq(mNestedScrollView), anyInt(), anyInt(),
+                anyInt(),
+                anyInt(), eq(ViewCompat.TYPE_NON_TOUCH), any(int[].class));
+        verify(mParent, atLeastOnce()).onStopNestedScroll(mNestedScrollView,
+                ViewCompat.TYPE_NON_TOUCH);
+    }
+
+    public static class NestedScrollingSpyView extends FrameLayout implements
+            NestedScrollingParent3 {
+
+        public OnStopNestedScrollListener mOnStopNestedScrollListener;
+
+        public NestedScrollingSpyView(Context context) {
+            super(context);
+        }
+
+        @Override
+        public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes,
+                int type) {
+            return false;
+        }
+
+        @Override
+        public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes,
+                int type) {
+
+        }
+
+        @Override
+        public void onStopNestedScroll(@NonNull View target, int type) {
+            if (mOnStopNestedScrollListener != null) {
+                mOnStopNestedScrollListener.onStopNestedScroll(type);
+            }
+        }
+
+        @Override
+        public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
+                int dxUnconsumed, int dyUnconsumed, int type) {
+
+        }
+
+        @Override
+        public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed,
+                int type) {
+
+        }
+
+        @Override
+        public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
+                int dxUnconsumed, int dyUnconsumed, int type, @Nullable int[] consumed) {
+        }
+
+        @Override
+        public void setNestedScrollingEnabled(boolean enabled) {
+
+        }
+
+        @Override
+        public boolean isNestedScrollingEnabled() {
+            return false;
+        }
+
+        @Override
+        public boolean startNestedScroll(int axes) {
+            return false;
+        }
+
+        @Override
+        public void stopNestedScroll() {
+
+        }
+
+        @Override
+        public boolean hasNestedScrollingParent() {
+            return false;
+        }
+
+        @Override
+        public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
+                int dyUnconsumed, int[] offsetInWindow) {
+            return false;
+        }
+
+        @Override
+        public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed,
+                int[] offsetInWindow) {
+            return false;
+        }
+
+        @Override
+        public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
+            return false;
+        }
+
+        @Override
+        public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
+            return false;
+        }
+
+        @Override
+        public boolean onStartNestedScroll(@NotNull View child, @NotNull View target, int axes) {
+            return false;
+        }
+
+        @Override
+        public void onNestedScrollAccepted(@NotNull View child, @NotNull View target, int axes) {
+
+        }
+
+        @Override
+        public void onStopNestedScroll(@NotNull View target) {
+
+        }
+
+        @Override
+        public void onNestedScroll(@NotNull View target, int dxConsumed, int dyConsumed,
+                int dxUnconsumed,
+                int dyUnconsumed) {
+
+        }
+
+        @Override
+        public void onNestedPreScroll(@NotNull View target, int dx, int dy,
+                @NotNull int[] consumed) {
+
+        }
+
+        @Override
+        public boolean onNestedFling(@NotNull View target, float velocityX, float velocityY,
+                boolean consumed) {
+            return false;
+        }
+
+        @Override
+        public boolean onNestedPreFling(@NotNull View target, float velocityX, float velocityY) {
+            return false;
+        }
+
+        @Override
+        public int getNestedScrollAxes() {
+            return 0;
+        }
+
+        interface OnStopNestedScrollListener {
+            void onStopNestedScroll(int type);
+        }
+    }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewScrollingTest.java b/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewNestedScrollingFlingTest.java
similarity index 78%
rename from core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewScrollingTest.java
rename to core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewNestedScrollingFlingTest.java
index e9aa6bf..443f2ed 100644
--- a/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewScrollingTest.java
+++ b/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewNestedScrollingFlingTest.java
@@ -34,13 +34,13 @@
 import android.os.SystemClock;
 import android.support.v4.BaseInstrumentationTestCase;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.test.R;
-import androidx.core.view.NestedScrollingChild3;
 import androidx.core.view.NestedScrollingParent3;
 import androidx.core.view.ViewCompat;
 import androidx.test.core.app.ApplicationProvider;
@@ -49,6 +49,8 @@
 import androidx.testutils.Direction;
 import androidx.testutils.SimpleGestureGeneratorKt;
 
+import org.jetbrains.annotations.NotNull;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -56,12 +58,12 @@
 import java.util.concurrent.TimeUnit;
 
 /**
- * Large integration tests that verify correct {@link NestedScrollView} scrolling behavior,
- * including interaction with nested scrolling.
-*/
+ * Large integration tests that verify correct NestedScrollView flinging behavior related to
+ * nested scrolling.
+ */
 @RunWith(AndroidJUnit4.class)
 @LargeTest
-public class NestedScrollViewScrollingTest extends
+public class NestedScrollViewNestedScrollingFlingTest extends
         BaseInstrumentationTestCase<TestContentViewActivity> {
 
     private static final int CHILD_HEIGHT = 800;
@@ -76,10 +78,45 @@
     private View mChild;
     private NestedScrollingSpyView mParent;
 
-    public NestedScrollViewScrollingTest() {
+    public NestedScrollViewNestedScrollingFlingTest() {
         super(TestContentViewActivity.class);
     }
 
+    @Before
+    public void setup() throws Throwable {
+        Context context = mActivityTestRule.getActivity();
+
+        mChild = new View(context);
+        mChild.setMinimumWidth(WIDTH);
+        mChild.setMinimumHeight(CHILD_HEIGHT);
+        mChild.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, CHILD_HEIGHT));
+        mChild.setBackgroundDrawable(
+                new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM,
+                        new int[]{0xFFFF0000, 0xFF00FF00}));
+
+        mNestedScrollView = new NestedScrollView(context);
+        mNestedScrollView.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, NSV_HEIGHT));
+        mNestedScrollView.setBackgroundColor(0xFF0000FF);
+        mNestedScrollView.addView(mChild);
+
+        mParent = spy(new NestedScrollingSpyView(context));
+        mParent.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, PARENT_HEIGHT));
+        mParent.setBackgroundColor(0xFF0000FF);
+        mParent.addView(mNestedScrollView);
+
+        // Attach to activity and wait for layouts.
+        final TestContentView testContentView =
+                mActivityTestRule.getActivity().findViewById(R.id.testContentView);
+        testContentView.expectLayouts(1);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                testContentView.addView(mParent);
+            }
+        });
+        testContentView.awaitLayouts(2);
+    }
+
     @Test
     public void onNestedFling_consumedIsFalse_animatesScroll() throws Throwable {
         onNestedFling_consumeParamDeterminesScroll(false, true);
@@ -92,9 +129,6 @@
 
     private void onNestedFling_consumeParamDeterminesScroll(final boolean consumeParamValue,
             final boolean scrolls) throws Throwable {
-        setup();
-        attachToActivity();
-
         final Context context = ApplicationProvider.getApplicationContext();
         final int targetVelocity = (int)
                 Math.ceil(SimpleGestureGeneratorKt.generateFlingData(context).getVelocity() * 1000);
@@ -131,9 +165,6 @@
 
     private void uiFlings_parentPreFlingReturnDeterminesNestedScrollViewsFling(
             final boolean returnValue, final boolean scrolls) throws Throwable {
-        setup();
-        attachToActivity();
-
         final CountDownLatch countDownLatch = new CountDownLatch(1);
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -171,11 +202,18 @@
     }
 
     @Test
-    public void uiFling_fullyParticipatesInNestedScrolling() throws Throwable {
-        setup();
-        attachToActivity();
+    public void uiFling_flingDoesNotReachEnd_fullyParticipatesInNestedScrolling() throws Throwable {
+        uiFling_fullyParticipatesInNestedScrolling(false);
+    }
 
-        final CountDownLatch countDownLatch = new CountDownLatch(2);
+    @Test
+    public void uiFling_flingReachesEnd_fullyParticipatesInNestedScrolling() throws Throwable {
+        uiFling_fullyParticipatesInNestedScrolling(true);
+    }
+
+    private void uiFling_fullyParticipatesInNestedScrolling(final boolean hardFling)
+            throws Throwable {
+        final CountDownLatch countDownLatch = new CountDownLatch(hardFling ? 2 : 1);
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
@@ -192,23 +230,37 @@
                             }
                         };
 
-                mNestedScrollView.setOnScrollChangeListener(
-                        new NestedScrollView.OnScrollChangeListener() {
-                            @Override
-                            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY,
-                                    int oldScrollX, int oldScrollY) {
-                                if (scrollY == TOTAL_SCROLL_DISTANCE) {
-                                    countDownLatch.countDown();
+                if (hardFling) {
+                    mNestedScrollView.setOnScrollChangeListener(
+                            new NestedScrollView.OnScrollChangeListener() {
+                                @Override
+                                public void onScrollChange(NestedScrollView v, int scrollX,
+                                        int scrollY,
+                                        int oldScrollX, int oldScrollY) {
+                                    if (scrollY == TOTAL_SCROLL_DISTANCE) {
+                                        countDownLatch.countDown();
+                                    }
                                 }
-                            }
-                        });
+                            });
+                }
+
+                ViewConfiguration configuration =
+                        ViewConfiguration.get(mActivityTestRule.getActivity());
+
+                float velocity;
+                if (hardFling) {
+                    velocity = configuration.getScaledMaximumFlingVelocity() * .9f;
+                } else {
+                    velocity = configuration.getScaledMinimumFlingVelocity() * 1.1f;
+                }
 
                 SimpleGestureGeneratorKt.simulateFling(
                         mNestedScrollView,
                         SystemClock.uptimeMillis(),
                         ORIGIN_X_Y,
                         ORIGIN_X_Y,
-                        Direction.UP);
+                        Direction.UP,
+                        velocity);
             }
         });
         assertThat(countDownLatch.await(2, TimeUnit.SECONDS), is(true));
@@ -220,11 +272,11 @@
                 ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
         verify(mParent, atLeastOnce()).onNestedPreScroll(eq(mNestedScrollView), anyInt(), anyInt(),
                 any(int[].class), eq(ViewCompat.TYPE_TOUCH));
-        verify(mParent, atLeastOnce()).onNestedScroll(eq(mNestedScrollView),  anyInt(), anyInt(),
+        verify(mParent, atLeastOnce()).onNestedScroll(eq(mNestedScrollView), anyInt(), anyInt(),
                 anyInt(), anyInt(), eq(ViewCompat.TYPE_TOUCH), any(int[].class));
-        verify(mParent, atLeastOnce()).onNestedPreFling(eq(mNestedScrollView),  anyFloat(),
+        verify(mParent, atLeastOnce()).onNestedPreFling(eq(mNestedScrollView), anyFloat(),
                 anyFloat());
-        verify(mParent, atLeastOnce()).onNestedFling(eq(mNestedScrollView),  anyFloat(), anyFloat(),
+        verify(mParent, atLeastOnce()).onNestedFling(eq(mNestedScrollView), anyFloat(), anyFloat(),
                 eq(true));
         verify(mParent, atLeastOnce()).onStopNestedScroll(mNestedScrollView, ViewCompat.TYPE_TOUCH);
 
@@ -235,7 +287,7 @@
                 ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);
         verify(mParent, atLeastOnce()).onNestedPreScroll(eq(mNestedScrollView), anyInt(), anyInt(),
                 any(int[].class), eq(ViewCompat.TYPE_NON_TOUCH));
-        verify(mParent, atLeastOnce()).onNestedScroll(eq(mNestedScrollView),  anyInt(), anyInt(),
+        verify(mParent, atLeastOnce()).onNestedScroll(eq(mNestedScrollView), anyInt(), anyInt(),
                 anyInt(),
                 anyInt(), eq(ViewCompat.TYPE_NON_TOUCH), any(int[].class));
         verify(mParent, atLeastOnce()).onStopNestedScroll(mNestedScrollView,
@@ -244,8 +296,6 @@
 
     @Test
     public void fling_fullyParticipatesInNestedScrolling() throws Throwable {
-        setup();
-        attachToActivity();
         final Context context = ApplicationProvider.getApplicationContext();
         final int targetVelocity = (int)
                 Math.ceil(SimpleGestureGeneratorKt.generateFlingData(context).getVelocity() * 1000);
@@ -290,7 +340,7 @@
                 ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);
         verify(mParent, atLeastOnce()).onNestedPreScroll(eq(mNestedScrollView), anyInt(), anyInt(),
                 any(int[].class), eq(ViewCompat.TYPE_NON_TOUCH));
-        verify(mParent, atLeastOnce()).onNestedScroll(eq(mNestedScrollView),  anyInt(), anyInt(),
+        verify(mParent, atLeastOnce()).onNestedScroll(eq(mNestedScrollView), anyInt(), anyInt(),
                 anyInt(),
                 anyInt(), eq(ViewCompat.TYPE_NON_TOUCH), any(int[].class));
         verify(mParent, atLeastOnce()).onStopNestedScroll(mNestedScrollView,
@@ -303,90 +353,15 @@
                 anyInt(), eq(ViewCompat.TYPE_TOUCH));
         verify(mParent, never()).onNestedPreScroll(any(View.class), anyInt(), anyInt(),
                 any(int[].class), eq(ViewCompat.TYPE_TOUCH));
-        verify(mParent, never()).onNestedScroll(any(View.class),  anyInt(), anyInt(), anyInt(),
+        verify(mParent, never()).onNestedScroll(any(View.class), anyInt(), anyInt(), anyInt(),
                 anyInt(), eq(ViewCompat.TYPE_TOUCH), any(int[].class));
-        verify(mParent, never()).onNestedPreFling(any(View.class),  anyFloat(), anyFloat());
-        verify(mParent, never()).onNestedFling(any(View.class),  anyFloat(), anyFloat(),
+        verify(mParent, never()).onNestedPreFling(any(View.class), anyFloat(), anyFloat());
+        verify(mParent, never()).onNestedFling(any(View.class), anyFloat(), anyFloat(),
                 anyBoolean());
         verify(mParent, never()).onStopNestedScroll(any(View.class), eq(ViewCompat.TYPE_TOUCH));
     }
 
-    @Test
-    public void smoothScrollBy_scrollsEntireDistanceIncludingMargins() throws Throwable {
-        setup();
-        setChildMargins(20, 30);
-        attachToActivity();
-
-        final CountDownLatch countDownLatch = new CountDownLatch(1);
-        final int expectedTarget = TOTAL_SCROLL_DISTANCE + 20 + 30;
-        final int scrollDistance = TOTAL_SCROLL_DISTANCE + 20 + 30;
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mNestedScrollView.setOnScrollChangeListener(
-                        new NestedScrollView.OnScrollChangeListener() {
-                            @Override
-                            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY,
-                                    int oldScrollX, int oldScrollY) {
-                                if (scrollY == expectedTarget) {
-                                    countDownLatch.countDown();
-                                }
-                            }
-                        });
-                mNestedScrollView.smoothScrollBy(0, scrollDistance);
-            }
-        });
-        assertThat(countDownLatch.await(2, TimeUnit.SECONDS), is(true));
-
-        assertThat(mNestedScrollView.getScrollY(), is(expectedTarget));
-    }
-
-    private void setup() {
-        Context context = mActivityTestRule.getActivity();
-
-        mChild = new View(context);
-        mChild.setMinimumWidth(WIDTH);
-        mChild.setMinimumHeight(CHILD_HEIGHT);
-        mChild.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, CHILD_HEIGHT));
-        mChild.setBackgroundDrawable(
-                new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM,
-                        new int[]{0xFFFF0000, 0xFF00FF00}));
-
-        mNestedScrollView = new NestedScrollView(context);
-        mNestedScrollView.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, NSV_HEIGHT));
-        mNestedScrollView.setBackgroundColor(0xFF0000FF);
-        mNestedScrollView.addView(mChild);
-
-        mParent = spy(new NestedScrollingSpyView(context));
-        mParent.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, PARENT_HEIGHT));
-        mParent.setBackgroundColor(0xFF0000FF);
-        mParent.addView(mNestedScrollView);
-    }
-
-    @SuppressWarnings("SameParameterValue")
-    private void setChildMargins(int top, int bottom) {
-        ViewGroup.LayoutParams currentLayoutParams = mChild.getLayoutParams();
-        NestedScrollView.LayoutParams childLayoutParams = new NestedScrollView.LayoutParams(
-                currentLayoutParams.width, currentLayoutParams.height);
-        childLayoutParams.topMargin = top;
-        childLayoutParams.bottomMargin = bottom;
-        mChild.setLayoutParams(childLayoutParams);
-    }
-
-    private void attachToActivity() throws Throwable {
-        final TestContentView testContentView =
-                mActivityTestRule.getActivity().findViewById(R.id.testContentView);
-        testContentView.expectLayouts(1);
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                testContentView.addView(mParent);
-            }
-        });
-        testContentView.awaitLayouts(2);
-    }
-
-    public static class NestedScrollingSpyView extends FrameLayout implements NestedScrollingChild3,
+    public static class NestedScrollingSpyView extends FrameLayout implements
             NestedScrollingParent3 {
 
         public OnStopNestedScrollListener mOnStopNestedScrollListener;
@@ -427,44 +402,11 @@
         }
 
         @Override
-        public boolean startNestedScroll(int axes, int type) {
-            return false;
-        }
-
-        @Override
-        public void stopNestedScroll(int type) {
-
-        }
-
-        @Override
-        public boolean hasNestedScrollingParent(int type) {
-            return false;
-        }
-
-        @Override
-        public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
-                int dyUnconsumed, @Nullable int[] offsetInWindow, int type) {
-            return false;
-        }
-
-        @Override
-        public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
-                @Nullable int[] offsetInWindow, int type) {
-            return false;
-        }
-
-        @Override
         public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
                 int dxUnconsumed, int dyUnconsumed, int type, @Nullable int[] consumed) {
         }
 
         @Override
-        public void dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
-                int dyUnconsumed, @Nullable int[] offsetInWindow, int type,
-                @NonNull int[] consumed) {
-        }
-
-        @Override
         public void setNestedScrollingEnabled(boolean enabled) {
 
         }
@@ -512,40 +454,40 @@
         }
 
         @Override
-        public boolean onStartNestedScroll(@NonNull  View child, @NonNull View target, int axes) {
+        public boolean onStartNestedScroll(@NotNull View child, @NotNull View target, int axes) {
             return false;
         }
 
         @Override
-        public void onNestedScrollAccepted(@NonNull  View child, @NonNull View target, int axes) {
+        public void onNestedScrollAccepted(@NotNull View child, @NotNull View target, int axes) {
 
         }
 
         @Override
-        public void onStopNestedScroll(@NonNull  View target) {
+        public void onStopNestedScroll(@NotNull View target) {
 
         }
 
         @Override
-        public void onNestedScroll(@NonNull  View target, int dxConsumed, int dyConsumed,
+        public void onNestedScroll(@NotNull View target, int dxConsumed, int dyConsumed,
                 int dxUnconsumed, int dyUnconsumed) {
 
         }
 
         @Override
-        public void onNestedPreScroll(
-                @NonNull  View target, int dx, int dy, @NonNull int[] consumed) {
+        public void onNestedPreScroll(@NotNull View target, int dx, int dy,
+                @NotNull int[] consumed) {
 
         }
 
         @Override
-        public boolean onNestedFling(@NonNull  View target, float velocityX, float velocityY,
+        public boolean onNestedFling(@NotNull View target, float velocityX, float velocityY,
                 boolean consumed) {
             return false;
         }
 
         @Override
-        public boolean onNestedPreFling(@NonNull  View target, float velocityX, float velocityY) {
+        public boolean onNestedPreFling(@NotNull View target, float velocityX, float velocityY) {
             return false;
         }
 
diff --git a/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewNestedScrollingFlingTest.kt b/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewNestedScrollingFlingVelocityTest.kt
similarity index 88%
rename from core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewNestedScrollingFlingTest.kt
rename to core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewNestedScrollingFlingVelocityTest.kt
index c954b90..8c43046 100644
--- a/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewNestedScrollingFlingTest.kt
+++ b/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewNestedScrollingFlingVelocityTest.kt
@@ -22,7 +22,6 @@
 import android.view.View
 import android.view.ViewGroup
 import android.widget.FrameLayout
-import androidx.core.view.NestedScrollingChild3
 import androidx.core.view.NestedScrollingParent3
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.filters.LargeTest
@@ -43,7 +42,7 @@
  */
 @RunWith(Parameterized::class)
 @LargeTest
-class NestedScrollViewNestedScrollingFlingTest(
+class NestedScrollViewNestedScrollingFlingVelocityTest(
     private val fingerDirectionUp: Boolean,
     private val parentIntercepts: Boolean,
     private val preScrollConsumption: Int,
@@ -127,16 +126,15 @@
 
         var flungVelocity = 0
 
-        constructor(context: Context) : super(context) {}
+        constructor(context: Context) : super(context)
 
-        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {}
+        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
 
         constructor(
             context: Context,
             attrs: AttributeSet?,
             defStyleAttr: Int
-        ) : super(context, attrs, defStyleAttr) {
-        }
+        ) : super(context, attrs, defStyleAttr)
 
         override fun fling(velocityY: Int) {
             flungVelocity = velocityY
@@ -144,7 +142,7 @@
     }
 
     inner class NestedScrollingParent(context: Context) : FrameLayout(context),
-        NestedScrollingChild3, NestedScrollingParent3 {
+        NestedScrollingParent3 {
 
         var preScrollY: Int = 0
         var postScrollY: Int = 0
@@ -193,37 +191,6 @@
             scrollBy(0, toScrollY)
         }
 
-        override fun startNestedScroll(axes: Int, type: Int): Boolean {
-            return false
-        }
-
-        override fun stopNestedScroll(type: Int) {}
-
-        override fun hasNestedScrollingParent(type: Int): Boolean {
-            return false
-        }
-
-        override fun dispatchNestedScroll(
-            dxConsumed: Int,
-            dyConsumed: Int,
-            dxUnconsumed: Int,
-            dyUnconsumed: Int,
-            offsetInWindow: IntArray?,
-            type: Int
-        ): Boolean {
-            return false
-        }
-
-        override fun dispatchNestedPreScroll(
-            dx: Int,
-            dy: Int,
-            consumed: IntArray?,
-            offsetInWindow: IntArray?,
-            type: Int
-        ): Boolean {
-            return false
-        }
-
         override fun onNestedScroll(
             target: View,
             dxConsumed: Int,
@@ -241,17 +208,6 @@
             scrollBy(0, toScrollY)
         }
 
-        override fun dispatchNestedScroll(
-            dxConsumed: Int,
-            dyConsumed: Int,
-            dxUnconsumed: Int,
-            dyUnconsumed: Int,
-            offsetInWindow: IntArray?,
-            type: Int,
-            consumed: IntArray
-        ) {
-        }
-
         override fun setNestedScrollingEnabled(enabled: Boolean) {}
 
         override fun isNestedScrollingEnabled(): Boolean {
diff --git a/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewSmoothScrollByTest.java b/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewSmoothScrollByTest.java
new file mode 100644
index 0000000..af6da42
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewSmoothScrollByTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.core.widget;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.GradientDrawable;
+import android.support.v4.BaseInstrumentationTestCase;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.core.test.R;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Large integration tests that verify correct NestedScrollView scrolling behavior,
+ * including interaction with nested scrolling.
+*/
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class NestedScrollViewSmoothScrollByTest extends
+        BaseInstrumentationTestCase<TestContentViewActivity> {
+
+    private static final int CHILD_HEIGHT = 800;
+    private static final int NSV_HEIGHT = 400;
+    private static final int WIDTH = 400;
+    private static final int TOTAL_SCROLL_DISTANCE = CHILD_HEIGHT - NSV_HEIGHT;
+
+    private NestedScrollView mNestedScrollView;
+    private View mChild;
+
+    public NestedScrollViewSmoothScrollByTest() {
+        super(TestContentViewActivity.class);
+    }
+
+    @Before
+    public void setup() {
+        Context context = mActivityTestRule.getActivity();
+
+        mChild = new View(context);
+        mChild.setMinimumWidth(WIDTH);
+        mChild.setMinimumHeight(CHILD_HEIGHT);
+        mChild.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, CHILD_HEIGHT));
+        mChild.setBackgroundDrawable(
+                new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM,
+                        new int[]{0xFFFF0000, 0xFF00FF00}));
+
+        mNestedScrollView = new NestedScrollView(context);
+        mNestedScrollView.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, NSV_HEIGHT));
+        mNestedScrollView.setBackgroundColor(0xFF0000FF);
+        mNestedScrollView.addView(mChild);
+    }
+
+    @Test
+    public void smoothScrollBy_scrollsEntireDistanceIncludingMargins() throws Throwable {
+        setChildMargins(20, 30);
+        attachToActivity();
+
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+        final int expectedTarget = TOTAL_SCROLL_DISTANCE + 20 + 30;
+        final int scrollDistance = TOTAL_SCROLL_DISTANCE + 20 + 30;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mNestedScrollView.setOnScrollChangeListener(
+                        new NestedScrollView.OnScrollChangeListener() {
+                            @Override
+                            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY,
+                                    int oldScrollX, int oldScrollY) {
+                                if (scrollY == expectedTarget) {
+                                    countDownLatch.countDown();
+                                }
+                            }
+                        });
+                mNestedScrollView.smoothScrollBy(0, scrollDistance);
+            }
+        });
+        assertThat(countDownLatch.await(2, TimeUnit.SECONDS), is(true));
+
+        assertThat(mNestedScrollView.getScrollY(), is(expectedTarget));
+    }
+
+    @SuppressWarnings("SameParameterValue")
+    private void setChildMargins(int top, int bottom) {
+        ViewGroup.LayoutParams currentLayoutParams = mChild.getLayoutParams();
+        NestedScrollView.LayoutParams childLayoutParams = new NestedScrollView.LayoutParams(
+                currentLayoutParams.width, currentLayoutParams.height);
+        childLayoutParams.topMargin = top;
+        childLayoutParams.bottomMargin = bottom;
+        mChild.setLayoutParams(childLayoutParams);
+    }
+
+    private void attachToActivity() throws Throwable {
+        final TestContentView testContentView =
+                mActivityTestRule.getActivity().findViewById(R.id.testContentView);
+        testContentView.expectLayouts(1);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                testContentView.addView(mNestedScrollView);
+            }
+        });
+        testContentView.awaitLayouts(2);
+    }
+}
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 9672a78..90d61c4 100644
--- a/core/core/src/main/java/androidx/core/app/ComponentActivity.java
+++ b/core/core/src/main/java/androidx/core/app/ComponentActivity.java
@@ -43,6 +43,7 @@
      *
      * <p>Note that these objects are not retained across configuration changes</p>
      */
+    @SuppressWarnings("deprecation")
     private SimpleArrayMap<Class<? extends ExtraData>, ExtraData> mExtraDataMap =
             new SimpleArrayMap<>();
 
@@ -54,8 +55,11 @@
      *
      * @see #getExtraData
      * @hide
+     * @deprecated Use {@link View#setTag(int, Object)} with the window's decor view.
      */
+    @SuppressWarnings("deprecation")
     @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @Deprecated
     public void putExtraData(ExtraData extraData) {
         mExtraDataMap.put(extraData.getClass(), extraData);
     }
@@ -65,9 +69,11 @@
      *
      * @see #putExtraData
      * @hide
+     * @deprecated Use {@link View#getTag(int)} with the window's decor view.
      */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
-    @SuppressWarnings("unchecked")
+    @SuppressWarnings({"unchecked", "deprecation"})
+    @Deprecated
     public <T extends ExtraData> T getExtraData(Class<T> extraDataClass) {
         return (T) mExtraDataMap.get(extraDataClass);
     }
@@ -101,8 +107,12 @@
 
     /**
      * @hide
+     * @deprecated Store the object you want to save directly by using
+     * {@link View#setTag(int, Object)} with the window's decor view.
      */
+    @SuppressWarnings("DeprecatedIsStillUsed")
     @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @Deprecated
     public static class ExtraData {
     }
 }
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
index 48bee26..5671074 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -4026,12 +4026,27 @@
         builder.append("; scrollable: " + isScrollable());
 
         builder.append("; [");
-        for (int actionBits = getActions(); actionBits != 0;) {
-            final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
-            actionBits &= ~action;
-            builder.append(getActionSymbolicName(action));
-            if (actionBits != 0) {
-                builder.append(", ");
+        if (Build.VERSION.SDK_INT >= 21) {
+            List<AccessibilityActionCompat> actions = getActionList();
+            for (int i = 0; i < actions.size(); i++) {
+                AccessibilityActionCompat action = actions.get(i);
+                String actionName = getActionSymbolicName(action.getId());
+                if (actionName.equals("ACTION_UNKNOWN") && action.getLabel() != null) {
+                    actionName = action.getLabel().toString();
+                }
+                builder.append(actionName);
+                if (i != actions.size() - 1) {
+                    builder.append(", ");
+                }
+            }
+        } else {
+            for (int actionBits = getActions(); actionBits != 0;) {
+                final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
+                actionBits &= ~action;
+                builder.append(getActionSymbolicName(action));
+                if (actionBits != 0) {
+                    builder.append(", ");
+                }
             }
         }
         builder.append("]");
@@ -4093,6 +4108,22 @@
                 return "ACTION_PASTE";
             case ACTION_SET_SELECTION:
                 return "ACTION_SET_SELECTION";
+            case android.R.id.accessibilityActionScrollUp:
+                return "ACTION_SCROLL_UP";
+            case android.R.id.accessibilityActionScrollLeft:
+                return "ACTION_SCROLL_LEFT";
+            case android.R.id.accessibilityActionScrollDown:
+                return "ACTION_SCROLL_DOWN";
+            case android.R.id.accessibilityActionScrollRight:
+                return "ACTION_SCROLL_RIGHT";
+            case android.R.id.accessibilityActionPageDown:
+                return "ACTION_PAGE_DOWN";
+            case android.R.id.accessibilityActionPageUp:
+                return "ACTION_PAGE_UP";
+            case android.R.id.accessibilityActionPageLeft:
+                return "ACTION_PAGE_LEFT";
+            case android.R.id.accessibilityActionPageRight:
+                return "ACTION_PAGE_RIGHT";
             default:
                 return"ACTION_UNKNOWN";
         }
diff --git a/core/core/src/main/java/androidx/core/widget/NestedScrollView.java b/core/core/src/main/java/androidx/core/widget/NestedScrollView.java
index c30a780..fff1cd1 100644
--- a/core/core/src/main/java/androidx/core/widget/NestedScrollView.java
+++ b/core/core/src/main/java/androidx/core/widget/NestedScrollView.java
@@ -945,7 +945,7 @@
                 final VelocityTracker velocityTracker = mVelocityTracker;
                 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                 int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
-                if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+                if ((Math.abs(initialVelocity) >= mMinimumVelocity)) {
                     if (!dispatchNestedPreFling(0, -initialVelocity)) {
                         dispatchNestedFling(0, -initialVelocity, true);
                         fling(-initialVelocity);
@@ -1410,6 +1410,17 @@
      * @param dy the number of pixels to scroll by on the Y axis
      */
     public final void smoothScrollBy(int dx, int dy) {
+        smoothScrollBy(dx, dy, false);
+    }
+
+    /**
+     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
+     *
+     * @param dx the number of pixels to scroll by on the X axis
+     * @param dy the number of pixels to scroll by on the Y axis
+     * @param withNestedScrolling whether to include nested scrolling operations.
+     */
+    private void smoothScrollBy(int dx, int dy, boolean withNestedScrolling) {
         if (getChildCount() == 0) {
             // Nothing to do.
             return;
@@ -1424,7 +1435,7 @@
             final int maxY = Math.max(0, childSize - parentSpace);
             dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY;
             mScroller.startScroll(getScrollX(), scrollY, 0, dy);
-            runAnimatedScroll(false);
+            runAnimatedScroll(withNestedScrolling);
         } else {
             if (!mScroller.isFinished()) {
                 abortAnimatedScroll();
@@ -1441,7 +1452,19 @@
      * @param y the position where to scroll on the Y axis
      */
     public final void smoothScrollTo(int x, int y) {
-        smoothScrollBy(x - getScrollX(), y - getScrollY());
+        smoothScrollTo(x, y, false);
+    }
+
+    /**
+     * Like {@link #scrollTo}, but scroll smoothly instead of immediately.
+     *
+     * @param x the position where to scroll on the X axis
+     * @param y the position where to scroll on the Y axis
+     * @param withNestedScrolling whether to include nested scrolling operations.
+     */
+    // This should be considered private, it is package private to avoid a synthetic ancestor.
+    void smoothScrollTo(int x, int y, boolean withNestedScrolling) {
+        smoothScrollBy(x - getScrollX(), y - getScrollY(), withNestedScrolling);
     }
 
     /**
@@ -1592,6 +1615,8 @@
 
         if (!mScroller.isFinished()) {
             ViewCompat.postInvalidateOnAnimation(this);
+        } else {
+            stopNestedScroll(ViewCompat.TYPE_NON_TOUCH);
         }
     }
 
@@ -2088,7 +2113,7 @@
                     final int targetScrollY = Math.min(nsvHost.getScrollY() + viewportHeight,
                             nsvHost.getScrollRange());
                     if (targetScrollY != nsvHost.getScrollY()) {
-                        nsvHost.smoothScrollTo(0, targetScrollY);
+                        nsvHost.smoothScrollTo(0, targetScrollY, true);
                         return true;
                     }
                 }
@@ -2098,7 +2123,7 @@
                             - nsvHost.getPaddingTop();
                     final int targetScrollY = Math.max(nsvHost.getScrollY() - viewportHeight, 0);
                     if (targetScrollY != nsvHost.getScrollY()) {
-                        nsvHost.smoothScrollTo(0, targetScrollY);
+                        nsvHost.smoothScrollTo(0, targetScrollY, true);
                         return true;
                     }
                 }
diff --git a/dynamic-animation/build.gradle b/dynamic-animation/build.gradle
index fe7bd58..6ad3e31 100644
--- a/dynamic-animation/build.gradle
+++ b/dynamic-animation/build.gradle
@@ -9,7 +9,7 @@
 }
 
 dependencies {
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.1.0")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/emoji/core/build.gradle b/emoji/core/build.gradle
index 0b811cb..4e13514 100644
--- a/emoji/core/build.gradle
+++ b/emoji/core/build.gradle
@@ -22,7 +22,7 @@
     // treats this as local jar and package it inside the aar.
     api files(configurations.repackage)
 
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     implementation(project(':collection:collection'))
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/exifinterface/OWNERS b/exifinterface/OWNERS
index 02f6139..d4d2d29 100644
--- a/exifinterface/OWNERS
+++ b/exifinterface/OWNERS
@@ -1,4 +1,5 @@
 gyumin@google.com
 hdmoon@google.com
 jinpark@google.com
-sungsoo@google.com
\ No newline at end of file
+klhyun@google.com
+sungsoo@google.com
diff --git a/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java b/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
index 66045dc..1a37b72 100644
--- a/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
+++ b/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
@@ -2927,7 +2927,7 @@
     // Names for the data formats for debugging purpose.
     static final String[] IFD_FORMAT_NAMES = new String[] {
             "", "BYTE", "STRING", "USHORT", "ULONG", "URATIONAL", "SBYTE", "UNDEFINED", "SSHORT",
-            "SLONG", "SRATIONAL", "SINGLE", "DOUBLE"
+            "SLONG", "SRATIONAL", "SINGLE", "DOUBLE", "IFD"
     };
     // Sizes of the components of each IFD value format
     static final int[] IFD_FORMAT_BYTES_PER_FORMAT = new int[] {
@@ -4451,7 +4451,10 @@
 
         FileInputStream in = null;
         FileOutputStream out = null;
-        File originalFile = new File(mFilename);
+        File originalFile = null;
+        if (mFilename != null) {
+            originalFile = new File(mFilename);
+        }
         File tempFile = null;
         try {
             // Move the original file to temporary file.
diff --git a/fragment/fragment-ktx/api/1.2.0-alpha02.txt b/fragment/fragment-ktx/api/1.2.0-alpha02.txt
index dd42d83..ab3b3b7 100644
--- a/fragment/fragment-ktx/api/1.2.0-alpha02.txt
+++ b/fragment/fragment-ktx/api/1.2.0-alpha02.txt
@@ -22,5 +22,10 @@
     method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> viewModels(androidx.fragment.app.Fragment, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer = { this }, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer = null);
   }
 
+  public final class ViewKt {
+    ctor public ViewKt();
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+  }
+
 }
 
diff --git a/fragment/fragment-ktx/api/current.txt b/fragment/fragment-ktx/api/current.txt
index dd42d83..ab3b3b7 100644
--- a/fragment/fragment-ktx/api/current.txt
+++ b/fragment/fragment-ktx/api/current.txt
@@ -22,5 +22,10 @@
     method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> viewModels(androidx.fragment.app.Fragment, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer = { this }, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer = null);
   }
 
+  public final class ViewKt {
+    ctor public ViewKt();
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+  }
+
 }
 
diff --git a/fragment/fragment-ktx/api/restricted_1.2.0-alpha02.txt b/fragment/fragment-ktx/api/restricted_1.2.0-alpha02.txt
index dd42d83..ab3b3b7 100644
--- a/fragment/fragment-ktx/api/restricted_1.2.0-alpha02.txt
+++ b/fragment/fragment-ktx/api/restricted_1.2.0-alpha02.txt
@@ -22,5 +22,10 @@
     method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> viewModels(androidx.fragment.app.Fragment, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer = { this }, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer = null);
   }
 
+  public final class ViewKt {
+    ctor public ViewKt();
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+  }
+
 }
 
diff --git a/fragment/fragment-ktx/api/restricted_current.txt b/fragment/fragment-ktx/api/restricted_current.txt
index dd42d83..ab3b3b7 100644
--- a/fragment/fragment-ktx/api/restricted_current.txt
+++ b/fragment/fragment-ktx/api/restricted_current.txt
@@ -22,5 +22,10 @@
     method @MainThread public static inline <reified VM extends androidx.lifecycle.ViewModel> kotlin.Lazy<VM> viewModels(androidx.fragment.app.Fragment, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelStoreOwner> ownerProducer = { this }, kotlin.jvm.functions.Function0<? extends androidx.lifecycle.ViewModelProvider.Factory>? factoryProducer = null);
   }
 
+  public final class ViewKt {
+    ctor public ViewKt();
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
+  }
+
 }
 
diff --git a/fragment/fragment-ktx/src/androidTest/java/androidx/fragment/app/ViewTest.kt b/fragment/fragment-ktx/src/androidTest/java/androidx/fragment/app/ViewTest.kt
new file mode 100644
index 0000000..d1eb53a
--- /dev/null
+++ b/fragment/fragment-ktx/src/androidTest/java/androidx/fragment/app/ViewTest.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.fragment.app
+
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.annotation.UiThreadTest
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Assert.fail
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ViewTest {
+    @get:Rule val activityRule = ActivityTestRule<TestActivity>(TestActivity::class.java)
+    private val fragmentManager get() = activityRule.activity.supportFragmentManager
+
+    @UiThreadTest
+    @Test
+    fun findFragment() {
+        val fragment = ViewFragment()
+        fragmentManager.commitNow {
+            add(android.R.id.content, fragment)
+        }
+
+        val foundFragment = fragment.requireView().findFragment<ViewFragment>()
+        assertWithMessage("View should have Fragment set")
+            .that(foundFragment)
+            .isSameInstanceAs(fragment)
+    }
+
+    @Test
+    fun findFragmentNull() {
+        val view = View(ApplicationProvider.getApplicationContext() as Context)
+        try {
+            view.findFragment<Fragment>()
+            fail("findFragment should throw IllegalStateException if a Fragment was not set")
+        } catch (e: IllegalStateException) {
+            assertThat(e)
+                .hasMessageThat().contains("View $view does not have a Fragment set")
+        }
+    }
+}
+
+class ViewFragment : Fragment() {
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        return View(context)
+    }
+}
diff --git a/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentViewModelLazy.kt b/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentViewModelLazy.kt
index f67b19a..b9bf7b5 100644
--- a/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentViewModelLazy.kt
+++ b/fragment/fragment-ktx/src/main/java/androidx/fragment/app/FragmentViewModelLazy.kt
@@ -20,7 +20,6 @@
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelLazy
 import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory
 import androidx.lifecycle.ViewModelProvider.Factory
 import androidx.lifecycle.ViewModelStore
 import androidx.lifecycle.ViewModelStoreOwner
@@ -61,7 +60,9 @@
 /**
  * Returns a property delegate to access parent activity's [ViewModel],
  * if [factoryProducer] is specified then [ViewModelProvider.Factory]
- * returned by it will be used to create [ViewModel] first time.
+ * returned by it will be used to create [ViewModel] first time. Otherwise, the activity's
+ * [androidx.activity.ComponentActivity.getDefaultViewModelProviderFactory](default factory)
+ * will be used.
  *
  * ```
  * class MyFragment : Fragment() {
@@ -75,7 +76,8 @@
 @MainThread
 inline fun <reified VM : ViewModel> Fragment.activityViewModels(
     noinline factoryProducer: (() -> Factory)? = null
-) = createViewModelLazy(VM::class, { requireActivity().viewModelStore }, factoryProducer)
+) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
+    factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })
 
 /**
  * Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer]
@@ -88,10 +90,7 @@
     factoryProducer: (() -> Factory)? = null
 ): Lazy<VM> {
     val factoryPromise = factoryProducer ?: {
-        val application = activity?.application ?: throw IllegalStateException(
-            "ViewModel can be accessed only when Fragment is attached"
-        )
-        AndroidViewModelFactory.getInstance(application)
+        defaultViewModelProviderFactory
     }
     return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
-}
+}
\ No newline at end of file
diff --git a/fragment/fragment-ktx/src/main/java/androidx/fragment/app/View.kt b/fragment/fragment-ktx/src/main/java/androidx/fragment/app/View.kt
new file mode 100644
index 0000000..63ae98f
--- /dev/null
+++ b/fragment/fragment-ktx/src/main/java/androidx/fragment/app/View.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.fragment.app
+
+import android.view.View
+
+/**
+ * Find a [Fragment] associated with a [View].
+ *
+ * This method will locate the [Fragment] associated with this view. This is automatically
+ * populated for the View returned by [Fragment.onCreateView] and its children.
+ *
+ * Calling this on a View that does not have a Fragment set will result in an
+ * [IllegalStateException]
+ */
+fun <F : Fragment> View.findFragment(): F = FragmentManager.findFragment(this)
diff --git a/fragment/fragment/api/1.2.0-alpha02.txt b/fragment/fragment/api/1.2.0-alpha02.txt
index 865189d..672cb24 100644
--- a/fragment/fragment/api/1.2.0-alpha02.txt
+++ b/fragment/fragment/api/1.2.0-alpha02.txt
@@ -25,7 +25,7 @@
     field public static final int STYLE_NO_TITLE = 1; // 0x1
   }
 
-  public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+  public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
     ctor public Fragment();
     ctor @ContentView public Fragment(@LayoutRes int);
     method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
@@ -36,6 +36,7 @@
     method public final android.os.Bundle? getArguments();
     method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
     method public android.content.Context? getContext();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
     method public Object? getEnterTransition();
     method public Object? getExitTransition();
     method public final androidx.fragment.app.FragmentManager? getFragmentManager();
@@ -269,6 +270,7 @@
     method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
     method public static void enableDebugLogging(boolean);
     method public boolean executePendingTransactions();
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
     method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
     method public androidx.fragment.app.Fragment? findFragmentByTag(String?);
     method public androidx.fragment.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
@@ -295,10 +297,10 @@
   }
 
   public static interface FragmentManager.BackStackEntry {
-    method public CharSequence? getBreadCrumbShortTitle();
-    method @StringRes public int getBreadCrumbShortTitleRes();
-    method public CharSequence? getBreadCrumbTitle();
-    method @StringRes public int getBreadCrumbTitleRes();
+    method @Deprecated public CharSequence? getBreadCrumbShortTitle();
+    method @Deprecated @StringRes public int getBreadCrumbShortTitleRes();
+    method @Deprecated public CharSequence? getBreadCrumbTitle();
+    method @Deprecated @StringRes public int getBreadCrumbTitleRes();
     method public int getId();
     method public String? getName();
   }
@@ -383,10 +385,10 @@
     method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment, String?);
     method public androidx.fragment.app.FragmentTransaction runOnCommit(Runnable);
     method @Deprecated public androidx.fragment.app.FragmentTransaction setAllowOptimization(boolean);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
     method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int);
     method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int);
     method public androidx.fragment.app.FragmentTransaction setMaxLifecycle(androidx.fragment.app.Fragment, androidx.lifecycle.Lifecycle.State);
diff --git a/fragment/fragment/api/current.txt b/fragment/fragment/api/current.txt
index 865189d..672cb24 100644
--- a/fragment/fragment/api/current.txt
+++ b/fragment/fragment/api/current.txt
@@ -25,7 +25,7 @@
     field public static final int STYLE_NO_TITLE = 1; // 0x1
   }
 
-  public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+  public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
     ctor public Fragment();
     ctor @ContentView public Fragment(@LayoutRes int);
     method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
@@ -36,6 +36,7 @@
     method public final android.os.Bundle? getArguments();
     method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
     method public android.content.Context? getContext();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
     method public Object? getEnterTransition();
     method public Object? getExitTransition();
     method public final androidx.fragment.app.FragmentManager? getFragmentManager();
@@ -269,6 +270,7 @@
     method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
     method public static void enableDebugLogging(boolean);
     method public boolean executePendingTransactions();
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
     method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
     method public androidx.fragment.app.Fragment? findFragmentByTag(String?);
     method public androidx.fragment.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
@@ -295,10 +297,10 @@
   }
 
   public static interface FragmentManager.BackStackEntry {
-    method public CharSequence? getBreadCrumbShortTitle();
-    method @StringRes public int getBreadCrumbShortTitleRes();
-    method public CharSequence? getBreadCrumbTitle();
-    method @StringRes public int getBreadCrumbTitleRes();
+    method @Deprecated public CharSequence? getBreadCrumbShortTitle();
+    method @Deprecated @StringRes public int getBreadCrumbShortTitleRes();
+    method @Deprecated public CharSequence? getBreadCrumbTitle();
+    method @Deprecated @StringRes public int getBreadCrumbTitleRes();
     method public int getId();
     method public String? getName();
   }
@@ -383,10 +385,10 @@
     method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment, String?);
     method public androidx.fragment.app.FragmentTransaction runOnCommit(Runnable);
     method @Deprecated public androidx.fragment.app.FragmentTransaction setAllowOptimization(boolean);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
     method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int);
     method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int);
     method public androidx.fragment.app.FragmentTransaction setMaxLifecycle(androidx.fragment.app.Fragment, androidx.lifecycle.Lifecycle.State);
diff --git a/fragment/fragment/api/restricted_1.2.0-alpha02.txt b/fragment/fragment/api/restricted_1.2.0-alpha02.txt
index e2cf936..b1f0d5f 100644
--- a/fragment/fragment/api/restricted_1.2.0-alpha02.txt
+++ b/fragment/fragment/api/restricted_1.2.0-alpha02.txt
@@ -26,7 +26,7 @@
     field public static final int STYLE_NO_TITLE = 1; // 0x1
   }
 
-  public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+  public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
     ctor public Fragment();
     ctor @ContentView public Fragment(@LayoutRes int);
     method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
@@ -37,6 +37,7 @@
     method public final android.os.Bundle? getArguments();
     method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
     method public android.content.Context? getContext();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
     method public Object? getEnterTransition();
     method public Object? getExitTransition();
     method public final androidx.fragment.app.FragmentManager? getFragmentManager();
@@ -274,6 +275,7 @@
     method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
     method public static void enableDebugLogging(boolean);
     method public boolean executePendingTransactions();
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
     method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
     method public androidx.fragment.app.Fragment? findFragmentByTag(String?);
     method public androidx.fragment.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
@@ -301,10 +303,10 @@
   }
 
   public static interface FragmentManager.BackStackEntry {
-    method public CharSequence? getBreadCrumbShortTitle();
-    method @StringRes public int getBreadCrumbShortTitleRes();
-    method public CharSequence? getBreadCrumbTitle();
-    method @StringRes public int getBreadCrumbTitleRes();
+    method @Deprecated public CharSequence? getBreadCrumbShortTitle();
+    method @Deprecated @StringRes public int getBreadCrumbShortTitleRes();
+    method @Deprecated public CharSequence? getBreadCrumbTitle();
+    method @Deprecated @StringRes public int getBreadCrumbTitleRes();
     method public int getId();
     method public String? getName();
   }
@@ -389,10 +391,10 @@
     method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment, String?);
     method public androidx.fragment.app.FragmentTransaction runOnCommit(Runnable);
     method @Deprecated public androidx.fragment.app.FragmentTransaction setAllowOptimization(boolean);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
     method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int);
     method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int);
     method public androidx.fragment.app.FragmentTransaction setMaxLifecycle(androidx.fragment.app.Fragment, androidx.lifecycle.Lifecycle.State);
diff --git a/fragment/fragment/api/restricted_current.txt b/fragment/fragment/api/restricted_current.txt
index e2cf936..b1f0d5f 100644
--- a/fragment/fragment/api/restricted_current.txt
+++ b/fragment/fragment/api/restricted_current.txt
@@ -26,7 +26,7 @@
     field public static final int STYLE_NO_TITLE = 1; // 0x1
   }
 
-  public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
+  public class Fragment implements android.content.ComponentCallbacks androidx.lifecycle.HasDefaultViewModelProviderFactory androidx.lifecycle.LifecycleOwner androidx.savedstate.SavedStateRegistryOwner android.view.View.OnCreateContextMenuListener androidx.lifecycle.ViewModelStoreOwner {
     ctor public Fragment();
     ctor @ContentView public Fragment(@LayoutRes int);
     method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
@@ -37,6 +37,7 @@
     method public final android.os.Bundle? getArguments();
     method public final androidx.fragment.app.FragmentManager getChildFragmentManager();
     method public android.content.Context? getContext();
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
     method public Object? getEnterTransition();
     method public Object? getExitTransition();
     method public final androidx.fragment.app.FragmentManager? getFragmentManager();
@@ -274,6 +275,7 @@
     method public void dump(String, java.io.FileDescriptor?, java.io.PrintWriter, String![]?);
     method public static void enableDebugLogging(boolean);
     method public boolean executePendingTransactions();
+    method public static <F extends androidx.fragment.app.Fragment> F findFragment(android.view.View);
     method public androidx.fragment.app.Fragment? findFragmentById(@IdRes int);
     method public androidx.fragment.app.Fragment? findFragmentByTag(String?);
     method public androidx.fragment.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
@@ -301,10 +303,10 @@
   }
 
   public static interface FragmentManager.BackStackEntry {
-    method public CharSequence? getBreadCrumbShortTitle();
-    method @StringRes public int getBreadCrumbShortTitleRes();
-    method public CharSequence? getBreadCrumbTitle();
-    method @StringRes public int getBreadCrumbTitleRes();
+    method @Deprecated public CharSequence? getBreadCrumbShortTitle();
+    method @Deprecated @StringRes public int getBreadCrumbShortTitleRes();
+    method @Deprecated public CharSequence? getBreadCrumbTitle();
+    method @Deprecated @StringRes public int getBreadCrumbTitleRes();
     method public int getId();
     method public String? getName();
   }
@@ -389,10 +391,10 @@
     method public androidx.fragment.app.FragmentTransaction replace(@IdRes int, androidx.fragment.app.Fragment, String?);
     method public androidx.fragment.app.FragmentTransaction runOnCommit(Runnable);
     method @Deprecated public androidx.fragment.app.FragmentTransaction setAllowOptimization(boolean);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
-    method public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbShortTitle(CharSequence?);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(@StringRes int);
+    method @Deprecated public androidx.fragment.app.FragmentTransaction setBreadCrumbTitle(CharSequence?);
     method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int);
     method public androidx.fragment.app.FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int, @AnimatorRes @AnimRes int);
     method public androidx.fragment.app.FragmentTransaction setMaxLifecycle(androidx.fragment.app.Fragment, androidx.lifecycle.Lifecycle.State);
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index c199145..777cb59 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -18,13 +18,14 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     api("androidx.collection:collection:1.1.0")
     api("androidx.viewpager:viewpager:1.0.0")
     api("androidx.loader:loader:1.0.0")
     api(project(":activity:activity"))
     api(project(":lifecycle:lifecycle-livedata-core"))
     api(project(":lifecycle:lifecycle-viewmodel"))
+    api(project(":lifecycle:lifecycle-viewmodel-savedstate"))
 
     androidTestImplementation(KOTLIN_STDLIB)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentContainerViewTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentContainerViewTest.kt
index 670ea6c..021ea3c 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentContainerViewTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentContainerViewTest.kt
@@ -35,20 +35,24 @@
 import androidx.testutils.waitForExecution
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Assert.fail
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import java.util.concurrent.CountDownLatch
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 class FragmentContainerViewTest {
     @get:Rule
     var activityRule = ActivityTestRule(FragmentTestActivity::class.java)
+    lateinit var context: Context
 
     @Before
     fun setupContainer() {
         activityRule.setContentView(R.layout.fragment_container_view)
+        context = activityRule.activity.applicationContext
     }
 
     @Test
@@ -98,8 +102,6 @@
     @SdkSuppress(minSdkVersion = 29) // WindowInsets.Builder requires API 29
     @Test
     fun windowInsetsDispatchToChildren() {
-        val context = activityRule.activity.applicationContext
-
         val parentView = FragmentContainerView(context)
         val childView = FragmentContainerView(context)
 
@@ -117,6 +119,8 @@
             insets
         }
 
+        childView.setTag(R.id.fragment_container_view_tag, Fragment())
+
         parentView.addView(childView)
         parentView.dispatchApplyWindowInsets(sentInsets)
 
@@ -124,18 +128,63 @@
     }
 
     @Test
-    fun removeViewAt() {
-        val context = activityRule.activity.applicationContext
-        val view = FragmentContainerView(context)
+    fun addView() {
+        val fm = activityRule.activity.supportFragmentManager
 
-        val childView1 = FragmentContainerView(context)
+        val view = View(context)
+        val fragment = Fragment()
+        fragment.mView = view
+
+        fm.setViewTag(fragment)
+
+        val fragmentContainerView = FragmentContainerView(context)
+
+        assertWithMessage("FragmentContainerView should have no child views")
+            .that(fragmentContainerView.childCount).isEqualTo(0)
+
+        fragmentContainerView.addView(view)
+
+        assertWithMessage("FragmentContainerView should have one child view")
+            .that(fragmentContainerView.childCount).isEqualTo(1)
+    }
+
+    @Test
+    fun addViewNotAssociatedWithFragment() {
+        val view = View(context)
+
+        try {
+            FragmentContainerView(context).addView(view, 0, null)
+            fail("View without a Fragment added to FragmentContainerView should throw an exception")
+        } catch (e: IllegalStateException) {
+            assertThat(e)
+                .hasMessageThat().contains(
+                    "Views added to a FragmentContainerView must be associated with a Fragment. " +
+                            "View " + view + " is not associated with a Fragment."
+                )
+        }
+    }
+
+    @Test
+    fun addViewInLayoutNotAssociatedWithFragment() {
+        val view = View(context)
+
+        try {
+            FragmentContainerView(context).addViewInLayout(view, 0, null, false)
+            fail("View without a Fragment added to FragmentContainerView should throw an exception")
+        } catch (e: IllegalStateException) {
+            assertThat(e)
+                .hasMessageThat().contains(
+                    "Views added to a FragmentContainerView must be associated with a Fragment. " +
+                            "View " + view + " is not associated with a Fragment."
+                )
+        }
+    }
+
+    @Test
+    fun removeViewAt() {
         val childView2 = FragmentContainerView(context)
 
-        view.addView(childView1)
-        view.addView(childView2)
-
-        assertThat(view.childCount).isEqualTo(2)
-        assertThat(view.getChildAt(1)).isEqualTo(childView2)
+        val view = setupRemoveTestsView(FragmentContainerView(context), childView2)
 
         view.removeViewAt(0)
 
@@ -145,17 +194,10 @@
 
     @Test
     fun removeViewInLayout() {
-        val context = activityRule.activity.applicationContext
-        val view = FragmentContainerView(context)
-
         val childView1 = FragmentContainerView(context)
         val childView2 = FragmentContainerView(context)
 
-        view.addView(childView1)
-        view.addView(childView2)
-
-        assertThat(view.childCount).isEqualTo(2)
-        assertThat(view.getChildAt(1)).isEqualTo(childView2)
+        val view = setupRemoveTestsView(childView1, childView2)
 
         view.removeViewInLayout(childView1)
 
@@ -165,17 +207,10 @@
 
     @Test
     fun removeView() {
-        val context = activityRule.activity.applicationContext
-        val view = FragmentContainerView(context)
-
         val childView1 = FragmentContainerView(context)
         val childView2 = FragmentContainerView(context)
 
-        view.addView(childView1)
-        view.addView(childView2)
-
-        assertThat(view.childCount).isEqualTo(2)
-        assertThat(view.getChildAt(1)).isEqualTo(childView2)
+        val view = setupRemoveTestsView(childView1, childView2)
 
         view.removeView(childView1)
 
@@ -184,17 +219,10 @@
 
     @Test
     fun removeViews() {
-        val context = activityRule.activity.applicationContext
-        val view = FragmentContainerView(context)
-
-        val childView1 = FragmentContainerView(context)
-        val childView2 = FragmentContainerView(context)
-
-        view.addView(childView1)
-        view.addView(childView2)
-
-        assertThat(view.childCount).isEqualTo(2)
-        assertThat(view.getChildAt(1)).isEqualTo(childView2)
+        val view = setupRemoveTestsView(
+            FragmentContainerView(context),
+            FragmentContainerView(context)
+        )
 
         view.removeViews(1, 1)
 
@@ -203,17 +231,10 @@
 
     @Test
     fun removeViewsInLayout() {
-        val context = activityRule.activity.applicationContext
-        val view = FragmentContainerView(context)
-
-        val childView1 = FragmentContainerView(context)
-        val childView2 = FragmentContainerView(context)
-
-        view.addView(childView1)
-        view.addView(childView2)
-
-        assertThat(view.childCount).isEqualTo(2)
-        assertThat(view.getChildAt(1)).isEqualTo(childView2)
+        val view = setupRemoveTestsView(
+            FragmentContainerView(context),
+            FragmentContainerView(context)
+        )
 
         view.removeViewsInLayout(1, 1)
 
@@ -222,17 +243,10 @@
 
     @Test
     fun removeAllViewsInLayout() {
-        val context = activityRule.activity.applicationContext
-        val view = FragmentContainerView(context)
-
-        val childView1 = FragmentContainerView(context)
-        val childView2 = FragmentContainerView(context)
-
-        view.addView(childView1)
-        view.addView(childView2)
-
-        assertThat(view.childCount).isEqualTo(2)
-        assertThat(view.getChildAt(1)).isEqualTo(childView2)
+        val view = setupRemoveTestsView(
+            FragmentContainerView(context),
+            FragmentContainerView(context)
+        )
 
         view.removeAllViewsInLayout()
 
@@ -242,22 +256,37 @@
     // removeDetachedView should not actually remove the view
     @Test
     fun removeDetachedView() {
-        val context = activityRule.activity.applicationContext
-        val view = FragmentContainerView(context)
-
         val childView1 = FragmentContainerView(context)
         val childView2 = FragmentContainerView(context)
 
+        val view = setupRemoveTestsView(childView1, childView2)
+
+        view.removeDetachedView(childView1, false)
+
+        assertThat(view.childCount).isEqualTo(2)
+        assertThat(view.getChildAt(1)).isEqualTo(childView2)
+    }
+
+    private fun setupRemoveTestsView(
+        childView1: FragmentContainerView,
+        childView2: FragmentContainerView
+    ): FragmentContainerView {
+        val view = FragmentContainerView(context)
+        val fragment1 = Fragment()
+        val fragment2 = Fragment()
+
+        fragment1.mView = childView1
+        fragment2.mView = childView2
+
+        childView1.setTag(R.id.fragment_container_view_tag, fragment1)
+        childView2.setTag(R.id.fragment_container_view_tag, fragment2)
+
         view.addView(childView1)
         view.addView(childView2)
 
         assertThat(view.childCount).isEqualTo(2)
         assertThat(view.getChildAt(1)).isEqualTo(childView2)
-
-        view.removeDetachedView(childView1, false)
-
-        assertThat(view.childCount).isEqualTo(2)
-        assertThat(view.getChildAt(1)).isEqualTo(childView2)
+        return view
     }
 
     // Disappearing child views should be drawn first before other child views.
@@ -275,6 +304,12 @@
         activityRule.waitForExecution()
 
         val frag1View = fragment1.mView as ChildView
+        // wait for the first draw to finish
+        drawnFirstCountDownLatch.await()
+
+        // reset the first drawn view for the transaction we care about.
+        drawnFirst = null
+        drawnFirstCountDownLatch = CountDownLatch(1)
 
         fm.beginTransaction()
             .setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right)
@@ -282,9 +317,8 @@
             .commit()
         activityRule.waitForExecution()
 
-        val frag2View = fragment2.mView as ChildView
-
-        assertThat(frag1View.drawTime).isLessThan(frag2View.drawTime)
+        drawnFirstCountDownLatch.await()
+        assertThat(drawnFirst!!).isEqualTo(frag1View)
     }
 
     class ChildViewFragment : StrictViewFragment() {
@@ -296,11 +330,20 @@
     }
 
     class ChildView(context: Context?) : View(context) {
-        var drawTime = 0L
-
         override fun onDraw(canvas: Canvas?) {
             super.onDraw(canvas)
-            drawTime = System.nanoTime()
+            setDrawnFirstView(this)
+        }
+    }
+
+    companion object {
+        var drawnFirst: View? = null
+        var drawnFirstCountDownLatch = CountDownLatch(1)
+        fun setDrawnFirstView(v: View) {
+            if (drawnFirst == null) {
+                drawnFirst = v
+            }
+            drawnFirstCountDownLatch.countDown()
         }
     }
 }
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionTest.kt
index 3df8b7e..8f852ff 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionTest.kt
@@ -175,6 +175,7 @@
         assertThat(onBackStackChangedTimes).isEqualTo(3)
 
         fragment1.waitForTransition()
+        fragment2.waitForTransition()
         val popBlue = findBlue()
         val popGreen = findGreen()
         verifyAndClearTransition(fragment1.reenterTransition, null, popBlue, popGreen)
@@ -762,6 +763,8 @@
         fragment2.waitForTransition()
         // It does not transition properly for ordered transactions, though.
         if (reorderingAllowed) {
+            // reordering allowed fragment3 to get a transition so we should wait for it to finish
+            fragment3.waitForTransition()
             verifyAndClearTransition(fragment2.returnTransition, null, midGreen, midBlue)
             val endGreen = findGreen()
             val endBlue = findBlue()
@@ -770,6 +773,8 @@
             verifyNoOtherTransitions(fragment2)
             verifyNoOtherTransitions(fragment3)
         } else {
+            // The pop transition will be executed so we should wait until fragment 1 finishes
+            fragment1.waitForTransition()
             // fragment3 doesn't get a transition since it conflicts with the pop transition
             verifyNoOtherTransitions(fragment3)
             // Everything else is just doing its best. Ordered transactions can't handle
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewTest.kt
index 06d3665..e65aa30 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentViewTest.kt
@@ -249,7 +249,7 @@
     // Removing a detached fragment should do nothing to the View and popping should bring
     // the Fragment back detached
     @Test
-    fun removeDetatchedView() {
+    fun removeDetachedView() {
         activityRule.setContentView(R.layout.simple_container)
         val container =
             activityRule.activity.findViewById<View>(R.id.fragmentContainer) as ViewGroup
@@ -310,6 +310,46 @@
             .isEqualTo(Lifecycle.State.INITIALIZED)
     }
 
+    @Test
+    fun findFragmentNoTagSet() {
+        val view = View(activityRule.activity)
+        try {
+            FragmentManager.findFragment<Fragment>(view)
+            fail("findFragment should throw IllegalStateException if a Fragment was not set")
+        } catch (e: IllegalStateException) {
+            assertThat(e)
+                .hasMessageThat().contains("View $view does not have a Fragment set")
+        }
+    }
+
+    @Test
+    fun findFragmentAfterAdd() {
+        activityRule.setContentView(R.layout.simple_container)
+        val fm = activityRule.activity.supportFragmentManager
+
+        val fragment = StrictViewFragment()
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit()
+        activityRule.executePendingTransactions()
+
+        assertThat(FragmentManager.findFragment<StrictViewFragment>(fragment.requireView()))
+            .isSameInstanceAs(fragment)
+    }
+
+    @Test
+    fun findFragmentFindByIdChildView() {
+        activityRule.setContentView(R.layout.simple_container)
+        val fm = activityRule.activity.supportFragmentManager
+
+        val fragment = StrictViewFragment(R.layout.fragment_a)
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit()
+        activityRule.executePendingTransactions()
+
+        val view = fragment.requireView().findViewById<View>(R.id.textA)
+        assertThat(view).isNotNull()
+        assertThat(FragmentManager.findFragment<StrictViewFragment>(view))
+            .isSameInstanceAs(fragment)
+    }
+
     // Hide a fragment and its View should be GONE. Then pop it and the View should be VISIBLE
     @Test
     fun hideFragment() {
@@ -492,7 +532,7 @@
 
     // Detaching a detached fragment should not throw
     @Test
-    fun detachDetatched() {
+    fun detachDetached() {
         activityRule.setContentView(R.layout.simple_container)
         val fm = activityRule.activity.supportFragmentManager
         val fragment = StrictViewFragment()
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/PostponedTransitionTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/PostponedTransitionTest.kt
index a9c1bee..9098d94 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/PostponedTransitionTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/PostponedTransitionTest.kt
@@ -1001,10 +1001,15 @@
         assertThat(start.sharedElementReturn.targets.size).isEqualTo(0)
         assertThat(end.sharedElementReturn.targets.size).isEqualTo(0)
 
-        val blue = end.requireView().findViewById<View>(R.id.blueSquare)
-        assertThat(end.sharedElementEnter.targets.contains(blue)).isTrue()
-        assertThat(end.sharedElementEnter.targets[0].transitionName).isEqualTo("blueSquare")
-        assertThat(end.sharedElementEnter.targets[1].transitionName).isEqualTo("blueSquare")
+        // This checks need to be done on the mainThread to ensure that the shared element draw is
+        // complete. If these checks are not on the mainThread, there is a chance that this gets
+        // checked during OnShotPreDrawListener.onPreDraw, causing the name assert to fail.
+        activityRule.runOnUiThread {
+            val blue = end.requireView().findViewById<View>(R.id.blueSquare)
+            assertThat(end.sharedElementEnter.targets.contains(blue)).isTrue()
+            assertThat(end.sharedElementEnter.targets[0].transitionName).isEqualTo("blueSquare")
+            assertThat(end.sharedElementEnter.targets[1].transitionName).isEqualTo("blueSquare")
+        }
 
         assertNoTargets(start)
         assertNoTargets(end)
@@ -1034,10 +1039,15 @@
         assertThat(start.sharedElementReturn.targets.size).isEqualTo(2)
         assertThat(end.sharedElementReturn.targets.size).isEqualTo(0)
 
-        val blue = end.requireView().findViewById<View>(R.id.blueSquare)
-        assertThat(start.sharedElementReturn.targets.contains(blue)).isTrue()
-        assertThat(start.sharedElementReturn.targets[0].transitionName).isEqualTo("blueSquare")
-        assertThat(start.sharedElementReturn.targets[1].transitionName).isEqualTo("blueSquare")
+        // This checks need to be done on the mainThread to ensure that the shared element draw is
+        // complete. If these checks are not on the mainThread, there is a chance that this gets
+        // checked during OnShotPreDrawListener.onPreDraw, causing the name assert to fail.
+        activityRule.runOnUiThread {
+            val blue = end.requireView().findViewById<View>(R.id.blueSquare)
+            assertThat(start.sharedElementReturn.targets.contains(blue)).isTrue()
+            assertThat(start.sharedElementReturn.targets[0].transitionName).isEqualTo("blueSquare")
+            assertThat(start.sharedElementReturn.targets[1].transitionName).isEqualTo("blueSquare")
+        }
 
         assertNoTargets(end)
         assertNoTargets(start)
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/PrimaryNavFragmentTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/PrimaryNavFragmentTest.kt
index 461e2d8..fc1f76d1 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/PrimaryNavFragmentTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/PrimaryNavFragmentTest.kt
@@ -17,10 +17,15 @@
 package androidx.fragment.app
 
 import android.app.Activity
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.ViewGroup
 import androidx.fragment.app.test.EmptyFragmentTestActivity
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.rule.ActivityTestRule
+import androidx.testutils.runOnUiThreadRethrow
+import androidx.testutils.waitForExecution
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Rule
 import org.junit.Test
@@ -254,11 +259,86 @@
             .isSameInstanceAs(strictFragment1)
     }
 
+    @Test
+    fun replacePostponedFragment() {
+        val fm = activityRule.activity.supportFragmentManager
+        val strictFragment = spy(StrictViewFragment())
+        val postponedFragment = spy(PostponedFragment())
+        val replacementFragment = spy(StrictFragment())
+        val inOrder = inOrder(strictFragment, postponedFragment, replacementFragment)
+
+        fm.beginTransaction()
+            .add(android.R.id.content, strictFragment)
+            .setPrimaryNavigationFragment(strictFragment)
+            .setReorderingAllowed(true)
+            .commit()
+        executePendingTransactions(fm)
+
+        inOrder.verify(strictFragment).onPrimaryNavigationFragmentChanged(true)
+        assertWithMessage("new fragment is not primary nav fragment")
+            .that(fm.primaryNavigationFragment)
+            .isSameInstanceAs(strictFragment)
+
+        fm.beginTransaction()
+            .replace(android.R.id.content, postponedFragment)
+            .setPrimaryNavigationFragment(postponedFragment)
+            .addToBackStack(null)
+            .setReorderingAllowed(true)
+            .commit()
+        activityRule.waitForExecution()
+
+        inOrder.verify(strictFragment).onPrimaryNavigationFragmentChanged(false)
+        inOrder.verify(postponedFragment).onPrimaryNavigationFragmentChanged(true)
+        inOrder.verify(postponedFragment).onPrimaryNavigationFragmentChanged(false)
+        inOrder.verify(strictFragment).onPrimaryNavigationFragmentChanged(true)
+        assertWithMessage("primary nav fragment not set correctly after replace")
+            .that(fm.primaryNavigationFragment)
+            .isSameInstanceAs(strictFragment)
+
+        // Now pop the back stack and also add a replacement Fragment
+        fm.popBackStack()
+        fm.beginTransaction()
+            .replace(android.R.id.content, replacementFragment)
+            .setPrimaryNavigationFragment(replacementFragment)
+            .setReorderingAllowed(true)
+            .addToBackStack(null)
+            .commit()
+        activityRule.waitForExecution()
+
+        inOrder.verify(strictFragment).onPrimaryNavigationFragmentChanged(false)
+        inOrder.verify(replacementFragment).onPrimaryNavigationFragmentChanged(true)
+        assertWithMessage("primary nav fragment not set correctly after replace")
+            .that(fm.primaryNavigationFragment)
+            .isSameInstanceAs(replacementFragment)
+
+        // Now go back to the first Fragment
+        activityRule.onBackPressed()
+
+        inOrder.verify(replacementFragment).onPrimaryNavigationFragmentChanged(false)
+        inOrder.verify(strictFragment).onPrimaryNavigationFragmentChanged(true)
+        assertWithMessage("primary nav fragment is restored after replace")
+            .that(fm.primaryNavigationFragment)
+            .isSameInstanceAs(strictFragment)
+        assertWithMessage("Only the first Fragment should exist on the FragmentManager")
+            .that(fm.fragments)
+            .containsExactly(strictFragment)
+    }
+
     private fun executePendingTransactions(fm: FragmentManager) {
         activityRule.runOnUiThread { fm.executePendingTransactions() }
     }
 
-    private fun ActivityTestRule<out Activity>.onBackPressed() = runOnUiThread {
+    private fun ActivityTestRule<out Activity>.onBackPressed() = runOnUiThreadRethrow {
         activity.onBackPressed()
     }
+
+    open class PostponedFragment : StrictViewFragment() {
+        override fun onCreateView(
+            inflater: LayoutInflater,
+            container: ViewGroup?,
+            savedInstanceState: Bundle?
+        ) = super.onCreateView(inflater, container, savedInstanceState).also {
+            postponeEnterTransition()
+        }
+    }
 }
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/TransitionFragment.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/TransitionFragment.kt
index d421abb..72dc1bb 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/TransitionFragment.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/TransitionFragment.kt
@@ -15,15 +15,12 @@
  */
 package androidx.fragment.app
 
-import android.os.SystemClock
 import android.transition.Transition
 import androidx.annotation.LayoutRes
 import androidx.fragment.test.R
-import org.mockito.ArgumentMatchers
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
 
 /**
  * A fragment that has transitions that can be tracked.
@@ -37,8 +34,25 @@
     val returnTransition = TrackingVisibility()
     val sharedElementEnter = TrackingTransition()
     val sharedElementReturn = TrackingTransition()
+    var startTransitionCountDownLatch = CountDownLatch(1)
+    var endTransitionCountDownLatch = CountDownLatch(1)
 
-    private val listener = mock(Transition.TransitionListener::class.java)
+    private val listener = object : Transition.TransitionListener {
+        override fun onTransitionEnd(transition: Transition) {
+            endTransitionCountDownLatch.countDown()
+            startTransitionCountDownLatch = CountDownLatch(1)
+        }
+
+        override fun onTransitionResume(transition: Transition) {}
+
+        override fun onTransitionPause(transition: Transition) {}
+
+        override fun onTransitionCancel(transition: Transition) {}
+
+        override fun onTransitionStart(transition: Transition) {
+            startTransitionCountDownLatch.countDown()
+        }
+    }
 
     init {
         @Suppress("LeakingThis")
@@ -60,18 +74,11 @@
     }
 
     internal fun waitForTransition() {
-        verify(
-            listener,
-            within(1000)
-        ).onTransitionEnd(ArgumentMatchers.any())
-        reset(listener)
+        endTransitionCountDownLatch.await()
+        endTransitionCountDownLatch = CountDownLatch(1)
     }
 
     internal fun waitForNoTransition() {
-        SystemClock.sleep(250)
-        verify(
-            listener,
-            never()
-        ).onTransitionStart(ArgumentMatchers.any())
+        assertThat(startTransitionCountDownLatch.await(250, TimeUnit.MILLISECONDS)).isFalse()
     }
 }
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTest.kt
index 28e73e2..3edf294 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTest.kt
@@ -108,20 +108,32 @@
             val fragmentModel = withActivity {
                 getFragment(ViewModelActivity.FRAGMENT_TAG_1).fragmentModel
             }
+            val fragmentAndroidModel = withActivity {
+                getFragment(ViewModelActivity.FRAGMENT_TAG_1).androidModel
+            }
+            val fragmentSavedStateAndroidModel = withActivity {
+                getFragment(ViewModelActivity.FRAGMENT_TAG_1).savedStateModel
+            }
             val backStackFragmentModel = withActivity {
                 getFragment(ViewModelActivity.FRAGMENT_TAG_BACK_STACK).fragmentModel
             }
             assertThat(fragmentModel.cleared).isFalse()
+            assertThat(fragmentAndroidModel.cleared).isFalse()
+            assertThat(fragmentSavedStateAndroidModel.cleared).isFalse()
             assertThat(backStackFragmentModel.cleared).isFalse()
 
             recreate()
             // recreate shouldn't clear the ViewModels
             assertThat(fragmentModel.cleared).isFalse()
+            assertThat(fragmentAndroidModel.cleared).isFalse()
+            assertThat(fragmentSavedStateAndroidModel.cleared).isFalse()
             assertThat(backStackFragmentModel.cleared).isFalse()
 
             moveToState(Lifecycle.State.DESTROYED)
             // But destroying the Activity should
             assertThat(fragmentModel.cleared).isTrue()
+            assertThat(fragmentAndroidModel.cleared).isTrue()
+            assertThat(fragmentSavedStateAndroidModel.cleared).isTrue()
             assertThat(backStackFragmentModel.cleared).isTrue()
         }
     }
@@ -134,10 +146,7 @@
                     supportFragmentManager.beginTransaction().add(it, "temp").commitNow()
                 }
             }
-            val viewModelProvider = ViewModelProvider(
-                fragment,
-                ViewModelProvider.NewInstanceFactory()
-            )
+            val viewModelProvider = ViewModelProvider(fragment)
             val vm = viewModelProvider.get(TestViewModel::class.java)
             assertThat(vm.cleared).isFalse()
             onActivity { activity ->
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTestInTransaction.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTestInTransaction.kt
index 03804cc..f281002 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTestInTransaction.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/ViewModelTestInTransaction.kt
@@ -20,7 +20,6 @@
 import androidx.fragment.app.test.EmptyFragmentTestActivity
 import androidx.fragment.app.test.TestViewModel
 import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.ViewModelStoreOwner
 import androidx.test.annotation.UiThreadTest
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -43,7 +42,7 @@
         val activity = activityRule.activity
         val fragment = TestFragment()
         activity.supportFragmentManager.beginTransaction().add(fragment, "tag").commitNow()
-        val viewModelProvider = ViewModelProvider(activity, ViewModelProvider.NewInstanceFactory())
+        val viewModelProvider = ViewModelProvider(activity)
         val viewModel = viewModelProvider.get(TestViewModel::class.java)
         assertThat(viewModel).isSameInstanceAs(fragment.viewModel)
     }
@@ -65,7 +64,7 @@
             super.onCreate(savedInstanceState)
             val fragment = TestFragment()
             childFragmentManager.beginTransaction().add(fragment, "tag").commitNow()
-            val viewModelProvider = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())
+            val viewModelProvider = ViewModelProvider(this)
             val viewModel = viewModelProvider.get(TestViewModel::class.java)
             assertThat(viewModel).isSameInstanceAs(fragment.viewModel)
             executed = true
@@ -79,10 +78,7 @@
         override fun onCreate(savedInstanceState: Bundle?) {
             super.onCreate(savedInstanceState)
             val parentFragment = parentFragment
-            val provider = ViewModelProvider(
-                (parentFragment ?: requireActivity()) as ViewModelStoreOwner,
-                ViewModelProvider.NewInstanceFactory()
-            )
+            val provider = ViewModelProvider(parentFragment ?: requireActivity())
             viewModel = provider.get(TestViewModel::class.java)
             assertThat(viewModel).isNotNull()
         }
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/test/ViewModelActivity.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/test/ViewModelActivity.kt
index a058649..b63f54d 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/test/ViewModelActivity.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/test/ViewModelActivity.kt
@@ -16,10 +16,14 @@
 
 package androidx.fragment.app.test
 
+import android.app.Application
 import android.os.Bundle
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentActivity
 import androidx.fragment.test.R
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelStore
 
@@ -48,10 +52,7 @@
                 .commit()
         }
 
-        val viewModelProvider = ViewModelProvider(
-            this,
-            ViewModelProvider.NewInstanceFactory()
-        )
+        val viewModelProvider = ViewModelProvider(this)
         activityModel = viewModelProvider.get(KEY_ACTIVITY_MODEL, TestViewModel::class.java)
         defaultActivityModel = viewModelProvider.get(TestViewModel::class.java)
     }
@@ -60,26 +61,41 @@
         lateinit var fragmentModel: TestViewModel
         lateinit var activityModel: TestViewModel
         lateinit var defaultActivityModel: TestViewModel
+        lateinit var androidModel: TestAndroidViewModel
+        lateinit var savedStateModel: TestSavedStateViewModel
 
         override fun onCreate(savedInstanceState: Bundle?) {
             super.onCreate(savedInstanceState)
-            val viewModelProvider = ViewModelProvider(
-                this,
-                ViewModelProvider.NewInstanceFactory()
-            )
+            val viewModelProvider = ViewModelProvider(this)
             fragmentModel = viewModelProvider.get(
                 KEY_FRAGMENT_MODEL,
                 TestViewModel::class.java
             )
-            val activityViewModelProvider = ViewModelProvider(
-                requireActivity(),
-                ViewModelProvider.NewInstanceFactory()
-            )
+            val activityViewModelProvider = ViewModelProvider(requireActivity())
             activityModel = activityViewModelProvider.get(
                 ViewModelActivity.KEY_ACTIVITY_MODEL,
                 TestViewModel::class.java
             )
             defaultActivityModel = activityViewModelProvider.get(TestViewModel::class.java)
+            androidModel = viewModelProvider.get(TestAndroidViewModel::class.java)
+            savedStateModel = viewModelProvider.get(TestSavedStateViewModel::class.java)
+        }
+    }
+
+    class TestAndroidViewModel(application: Application) : AndroidViewModel(application) {
+        var cleared = false
+
+        override fun onCleared() {
+            cleared = true
+        }
+    }
+
+    @Suppress("unused")
+    class TestSavedStateViewModel(val savedStateHandle: SavedStateHandle) : ViewModel() {
+        var cleared = false
+
+        override fun onCleared() {
+            cleared = true
         }
     }
 
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java b/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
index 050db60..ccbc082 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
@@ -314,7 +314,7 @@
         }
         mCommitted = true;
         if (mAddToBackStack) {
-            mIndex = mManager.allocBackStackIndex(this);
+            mIndex = mManager.allocBackStackIndex();
         } else {
             mIndex = -1;
         }
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
index f6efea6..4dc499c 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
@@ -59,12 +59,15 @@
 import androidx.core.app.SharedElementCallback;
 import androidx.core.util.DebugUtils;
 import androidx.core.view.LayoutInflaterCompat;
+import androidx.lifecycle.HasDefaultViewModelProviderFactory;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleEventObserver;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.SavedStateViewModelFactory;
+import androidx.lifecycle.ViewModelProvider;
 import androidx.lifecycle.ViewModelStore;
 import androidx.lifecycle.ViewModelStoreOwner;
 import androidx.loader.app.LoaderManager;
@@ -94,7 +97,7 @@
  *
  */
 public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
-        ViewModelStoreOwner, SavedStateRegistryOwner {
+        ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner {
 
     static final Object USE_DEFAULT_TRANSITION = new Object();
 
@@ -267,6 +270,8 @@
     @Nullable FragmentViewLifecycleOwner mViewLifecycleOwner;
     MutableLiveData<LifecycleOwner> mViewLifecycleOwnerLiveData = new MutableLiveData<>();
 
+    private ViewModelProvider.Factory mDefaultFactory;
+
     SavedStateRegistryController mSavedStateRegistryController;
 
     @LayoutRes
@@ -365,6 +370,28 @@
         return mFragmentManager.getViewModelStore(this);
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The {@link #getArguments() Fragment's arguments} when this is first called will be used
+     * as the defaults to any {@link androidx.lifecycle.SavedStateHandle} passed to a view model
+     * created using this factory.</p>
+     */
+    @NonNull
+    @Override
+    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
+        if (mFragmentManager == null) {
+            throw new IllegalStateException("Can't access ViewModels from detached fragment");
+        }
+        if (mDefaultFactory == null) {
+            mDefaultFactory = new SavedStateViewModelFactory(
+                    requireActivity().getApplication(),
+                    this,
+                    getArguments());
+        }
+        return mDefaultFactory;
+    }
+
     @NonNull
     @Override
     public final SavedStateRegistry getSavedStateRegistry() {
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentContainerView.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentContainerView.java
index 976c7e5..898a17c 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentContainerView.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentContainerView.java
@@ -19,8 +19,11 @@
 import android.animation.LayoutTransition;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.os.Bundle;
 import android.util.AttributeSet;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
 
@@ -34,6 +37,26 @@
  * {@link FrameLayout}, so it can reliably handle Fragment Transactions, and it also has additional
  * features to coordinate with fragment behavior.
  *
+ * <p>FragmentContainerView should be used as the container for Fragments, commonly set in the
+ * xml layout of an activity, e.g.: <p>
+ *
+ * <pre class="prettyprint">
+ * &lt;androidx.fragment.app.FragmentContainerView
+ *        xmlns:android="http://schemas.android.com/apk/res/android"
+ *        xmlns:app="http://schemas.android.com/apk/res-auto"
+ *        android:id="@+id/fragment_container_view"
+ *        android:layout_width="match_parent"
+ *        android:layout_height="match_parent"&gt;
+ * &lt;/androidx.fragment.app.FragmentContainerView&gt;
+ * </pre>
+ *
+ * <p>FragmentContainerView should not be used as a replacement for other ViewGroups (FrameLayout,
+ * LinearLayout, etc) outside of Fragment use cases.
+ *
+ * <p>FragmentContainerView will only allow views to returned by a Fragment's
+ * {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}. Attempting to add any other
+ * view will result in an {@link IllegalStateException}.
+ *
  * <p>Layout animations and transitions are disabled for FragmentContainerView. Animations should be
  * done through {@link FragmentTransaction#setCustomAnimations(int, int, int, int)}. If
  * animateLayoutChanges is set to <code>true</code> or
@@ -41,9 +64,7 @@
  * {@link UnsupportedOperationException} will be thrown.
  *
  * <p>Fragments using exit animations are drawn before all others for FragmentContainerView. This
- * ensures that exiting Fragments do not appear on top of the view. When using this layout, a
- * Fragment with an enter animation is popped using {@link FragmentManager#popBackStack()}, the
- * reverse animation will not appear.
+ * ensures that exiting Fragments do not appear on top of the view.
  */
 public class FragmentContainerView extends FrameLayout {
 
@@ -145,6 +166,41 @@
         super.endViewTransition(view);
     }
 
+    /**
+     * <p>FragmentContainerView will only allow views to returned by a Fragment's
+     * {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}. Attempting to add any
+     *  other view will result in an {@link IllegalStateException}.
+     *
+     * {@inheritDoc}
+     */
+    @Override
+    public void addView(@NonNull View child, int index, @Nullable ViewGroup.LayoutParams params) {
+        if (FragmentManager.getViewFragment(child) == null) {
+            throw new IllegalStateException("Views added to a FragmentContainerView must be"
+                    + " associated with a Fragment. View " + child + " is not associated with a"
+                    + " Fragment.");
+        }
+        super.addView(child, index, params);
+    }
+
+    /**
+     * <p>FragmentContainerView will only allow views to returned by a Fragment's
+     * {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}. Attempting to add any
+     *  other view will result in an {@link IllegalStateException}.
+     *
+     * {@inheritDoc}
+     */
+    @Override
+    protected boolean addViewInLayout(@NonNull View child, int index,
+            @Nullable ViewGroup.LayoutParams params, boolean preventRequestLayout) {
+        if (FragmentManager.getViewFragment(child) == null) {
+            throw new IllegalStateException("Views added to a FragmentContainerView must be"
+                    + " associated with a Fragment. View " + child + " is not associated with a"
+                    + " Fragment.");
+        }
+        return super.addViewInLayout(child, index, params, preventRequestLayout);
+    }
+
     @Override
     public void removeViewAt(int index) {
         View view = getChildAt(index);
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
index 4282de9..d376369 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -36,6 +36,7 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewParent;
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
 import android.view.animation.AnimationUtils;
@@ -65,12 +66,12 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Static library support version of the framework's {@link android.app.FragmentManager}.
@@ -136,28 +137,36 @@
         /**
          * Return the full bread crumb title resource identifier for the entry,
          * or 0 if it does not have one.
+         * @deprecated Store breadcrumb titles separately from back stack entries.
          */
+        @Deprecated
         @StringRes
         int getBreadCrumbTitleRes();
 
         /**
          * Return the short bread crumb title resource identifier for the entry,
          * or 0 if it does not have one.
+         * @deprecated Store breadcrumb short titles separately from back stack entries.
          */
+        @Deprecated
         @StringRes
         int getBreadCrumbShortTitleRes();
 
         /**
          * Return the full bread crumb title for the entry, or null if it
          * does not have one.
+         * @deprecated Store breadcrumb titles separately from back stack entries.
          */
+        @Deprecated
         @Nullable
         CharSequence getBreadCrumbTitle();
 
         /**
          * Return the short bread crumb title for the entry, or null if it
          * does not have one.
+         * @deprecated Store breadcrumb short titles separately from back stack entries.
          */
+        @Deprecated
         @Nullable
         CharSequence getBreadCrumbShortTitle();
     }
@@ -343,8 +352,6 @@
     private final ArrayList<OpGenerator> mPendingActions = new ArrayList<>();
     private boolean mExecutingActions;
 
-    private int mNextFragmentIndex = 0;
-
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     final ArrayList<Fragment> mAdded = new ArrayList<>();
     final HashMap<String, Fragment> mActive = new HashMap<>();
@@ -361,9 +368,7 @@
         }
     };
 
-    // Must be accessed while locked.
-    private final ArrayList<BackStackRecord> mBackStackIndices = new ArrayList<>();
-    private final ArrayList<Integer> mAvailBackStackIndices = new ArrayList<>();
+    private final AtomicInteger mBackStackIndex = new AtomicInteger();
 
     private ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
     private final CopyOnWriteArrayList<FragmentLifecycleCallbacksHolder>
@@ -749,6 +754,69 @@
     }
 
     /**
+     * Find a {@link Fragment} associated with the given {@link View}.
+     *
+     * This method will locate the {@link Fragment} associated with this view. This is automatically
+     * populated for the View returned by {@link Fragment#onCreateView} and its children.
+     *
+     * @param view the view to search from
+     * @return the locally scoped {@link Fragment} to the given view
+     * @throws IllegalStateException if the given view does not correspond with a
+     * {@link Fragment}.
+     */
+    @NonNull
+    @SuppressWarnings("unchecked") // We should throw a ClassCast exception if the type is wrong
+    public static <F extends Fragment> F findFragment(@NonNull View view) {
+        Fragment fragment = findViewFragment(view);
+        if (fragment == null) {
+            throw new IllegalStateException("View " + view + " does not have a Fragment set");
+        }
+        return (F) fragment;
+    }
+
+    /**
+     * Recurse up the view hierarchy, looking for the Fragment
+     * @param view the view to search from
+     * @return the locally scoped {@link Fragment} to the given view, if found
+     */
+    @Nullable
+    private static Fragment findViewFragment(@NonNull View view) {
+        while (view != null) {
+            Fragment fragment = getViewFragment(view);
+            if (fragment != null) {
+                return fragment;
+            }
+            ViewParent parent = view.getParent();
+            view = parent instanceof View ? (View) parent : null;
+        }
+        return null;
+    }
+
+    /**
+     * Check if this view has an associated Fragment
+     * @param view the view to search from
+     * @return the locally scoped {@link Fragment} to the given view, if found
+     */
+    @Nullable
+    static Fragment getViewFragment(@NonNull View view) {
+        Object tag = view.getTag(R.id.fragment_container_view_tag);
+        if (tag instanceof Fragment) {
+            return (Fragment) tag;
+        }
+        return null;
+    }
+
+    /**
+     * Used to store the Fragment inside of its view's tag. This is done after the fragment's view
+     * is created, but before the view is added to the container.
+     *
+     * @param fragment The fragment to be set as a tag on its view
+     */
+    void setViewTag(@NonNull Fragment fragment) {
+        fragment.mView.setTag(R.id.fragment_container_view_tag, fragment);
+    }
+
+    /**
      * Get a list of all fragments that are currently added to the FragmentManager.
      * This may include those that are hidden as well as those that are shown.
      * This will not include any fragments only in the back stack, or fragments that
@@ -955,26 +1023,8 @@
             }
         }
 
-        synchronized (mBackStackIndices) {
-            count = mBackStackIndices.size();
-            if (count > 0) {
-                writer.print(prefix); writer.println("Back Stack Indices:");
-                for (int i = 0; i < count; i++) {
-                    BackStackRecord bs = mBackStackIndices.get(i);
-                    writer.print(prefix);
-                    writer.print("  #");
-                    writer.print(i);
-                    writer.print(": ");
-                    writer.println(bs);
-                }
-            }
-
-            if (!mAvailBackStackIndices.isEmpty()) {
-                writer.print(prefix);
-                writer.print("mAvailBackStackIndices: ");
-                writer.println(Arrays.toString(mAvailBackStackIndices.toArray()));
-            }
-        }
+        writer.print(prefix);
+        writer.println("Back Stack Index: " + mBackStackIndex.get());
 
         synchronized (mPendingActions) {
             count = mPendingActions.size();
@@ -1273,6 +1323,7 @@
                             if (f.mView != null) {
                                 f.mInnerView = f.mView;
                                 f.mView.setSaveFromParentEnabled(false);
+                                setViewTag(f);
                                 if (container != null) {
                                     container.addView(f.mView);
                                 }
@@ -2010,48 +2061,8 @@
         }
     }
 
-    int allocBackStackIndex(BackStackRecord bse) {
-        synchronized (mBackStackIndices) {
-            if (mAvailBackStackIndices.isEmpty()) {
-                int index = mBackStackIndices.size();
-                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
-                mBackStackIndices.add(bse);
-                return index;
-
-            } else {
-                int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size() - 1);
-                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
-                mBackStackIndices.set(index, bse);
-                return index;
-            }
-        }
-    }
-
-    private void setBackStackIndex(int index, BackStackRecord bse) {
-        synchronized (mBackStackIndices) {
-            int count = mBackStackIndices.size();
-            if (index < count) {
-                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
-                mBackStackIndices.set(index, bse);
-            } else {
-                while (count < index) {
-                    mBackStackIndices.add(null);
-                    if (DEBUG) Log.v(TAG, "Adding available back stack index " + count);
-                    mAvailBackStackIndices.add(count);
-                    count++;
-                }
-                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
-                mBackStackIndices.add(bse);
-            }
-        }
-    }
-
-    private void freeBackStackIndex(int index) {
-        synchronized (mBackStackIndices) {
-            mBackStackIndices.set(index, null);
-            if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
-            mAvailBackStackIndices.add(index);
-        }
+    int allocBackStackIndex() {
+        return mBackStackIndex.getAndIncrement();
     }
 
     /**
@@ -2155,6 +2166,9 @@
             if (records != null && !listener.mIsBack) {
                 int index = records.indexOf(listener.mRecord);
                 if (index != -1 && isRecordPop.get(index)) {
+                    mPostponedTransactions.remove(i);
+                    i--;
+                    numPostponed--;
                     listener.cancelTransaction();
                     continue;
                 }
@@ -2290,7 +2304,6 @@
             final BackStackRecord record = records.get(recordNum);
             final boolean isPop = isRecordPop.get(recordNum);
             if (isPop && record.mIndex >= 0) {
-                freeBackStackIndex(record.mIndex);
                 record.mIndex = -1;
             }
             record.runOnCommitRunnables();
@@ -2824,10 +2837,10 @@
         fms.mActive = active;
         fms.mAdded = added;
         fms.mBackStack = backStack;
+        fms.mBackStackIndex = mBackStackIndex.get();
         if (mPrimaryNav != null) {
             fms.mPrimaryNavActiveWho = mPrimaryNav.mWho;
         }
-        fms.mNextFragmentIndex = mNextFragmentIndex;
         return fms;
     }
 
@@ -2936,19 +2949,16 @@
                     pw.close();
                 }
                 mBackStack.add(bse);
-                if (bse.mIndex >= 0) {
-                    setBackStackIndex(bse.mIndex, bse);
-                }
             }
         } else {
             mBackStack = null;
         }
+        mBackStackIndex.set(fms.mBackStackIndex);
 
         if (fms.mPrimaryNavActiveWho != null) {
             mPrimaryNav = mActive.get(fms.mPrimaryNavActiveWho);
             dispatchParentPrimaryNavigationFragmentChanged(mPrimaryNav);
         }
-        this.mNextFragmentIndex = fms.mNextFragmentIndex;
     }
 
     /**
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManagerState.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManagerState.java
index 969c3bc..a3dbe08 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManagerState.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManagerState.java
@@ -27,8 +27,8 @@
     ArrayList<FragmentState> mActive;
     ArrayList<String> mAdded;
     BackStackState[] mBackStack;
+    int mBackStackIndex;
     String mPrimaryNavActiveWho = null;
-    int mNextFragmentIndex;
 
     public FragmentManagerState() {
     }
@@ -37,8 +37,8 @@
         mActive = in.createTypedArrayList(FragmentState.CREATOR);
         mAdded = in.createStringArrayList();
         mBackStack = in.createTypedArray(BackStackState.CREATOR);
+        mBackStackIndex = in.readInt();
         mPrimaryNavActiveWho = in.readString();
-        mNextFragmentIndex = in.readInt();
     }
 
     @Override
@@ -51,8 +51,8 @@
         dest.writeTypedList(mActive);
         dest.writeStringList(mAdded);
         dest.writeTypedArray(mBackStack, flags);
+        dest.writeInt(mBackStackIndex);
         dest.writeString(mPrimaryNavActiveWho);
-        dest.writeInt(mNextFragmentIndex);
     }
 
     public static final Parcelable.Creator<FragmentManagerState> CREATOR
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java
index d161dfb..f92b54d 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentTransaction.java
@@ -655,7 +655,9 @@
      * is on the back stack.
      *
      * @param res A string resource containing the title.
+     * @deprecated Store breadcrumb titles separately from fragment transactions.
      */
+    @Deprecated
     @NonNull
     public FragmentTransaction setBreadCrumbTitle(@StringRes int res) {
         mBreadCrumbTitleRes = res;
@@ -667,7 +669,9 @@
      * Like {@link #setBreadCrumbTitle(int)} but taking a raw string; this
      * method is <em>not</em> recommended, as the string can not be changed
      * later if the locale changes.
+     * @deprecated Store breadcrumb titles separately from fragment transactions.
      */
+    @Deprecated
     @NonNull
     public FragmentTransaction setBreadCrumbTitle(@Nullable CharSequence text) {
         mBreadCrumbTitleRes = 0;
@@ -680,7 +684,9 @@
      * is on the back stack.
      *
      * @param res A string resource containing the title.
+     * @deprecated Store breadcrumb short titles separately from fragment transactions.
      */
+    @Deprecated
     @NonNull
     public FragmentTransaction setBreadCrumbShortTitle(@StringRes int res) {
         mBreadCrumbShortTitleRes = res;
@@ -692,7 +698,9 @@
      * Like {@link #setBreadCrumbShortTitle(int)} but taking a raw string; this
      * method is <em>not</em> recommended, as the string can not be changed
      * later if the locale changes.
+     * @deprecated Store breadcrumb short titles separately from fragment transactions.
      */
+    @Deprecated
     @NonNull
     public FragmentTransaction setBreadCrumbShortTitle(@Nullable CharSequence text) {
         mBreadCrumbShortTitleRes = 0;
diff --git a/fragment/fragment/src/main/res/anim-v21/fast_out_extra_slow_in.xml b/fragment/fragment/src/main/res/anim-v21/fragment_fast_out_extra_slow_in.xml
similarity index 100%
rename from fragment/fragment/src/main/res/anim-v21/fast_out_extra_slow_in.xml
rename to fragment/fragment/src/main/res/anim-v21/fragment_fast_out_extra_slow_in.xml
diff --git a/fragment/fragment/src/main/res/anim/fragment_close_enter.xml b/fragment/fragment/src/main/res/anim/fragment_close_enter.xml
index 0a2d2b9..3b799e1 100644
--- a/fragment/fragment/src/main/res/anim/fragment_close_enter.xml
+++ b/fragment/fragment/src/main/res/anim/fragment_close_enter.xml
@@ -28,6 +28,6 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@anim/fast_out_extra_slow_in"
+        android:interpolator="@anim/fragment_fast_out_extra_slow_in"
         android:duration="400"/>
 </set>
\ No newline at end of file
diff --git a/fragment/fragment/src/main/res/anim/fragment_close_exit.xml b/fragment/fragment/src/main/res/anim/fragment_close_exit.xml
index 012c886..c540f93 100644
--- a/fragment/fragment/src/main/res/anim/fragment_close_exit.xml
+++ b/fragment/fragment/src/main/res/anim/fragment_close_exit.xml
@@ -38,6 +38,6 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@anim/fast_out_extra_slow_in"
+        android:interpolator="@anim/fragment_fast_out_extra_slow_in"
         android:duration="400"/>
 </set>
\ No newline at end of file
diff --git a/fragment/fragment/src/main/res/anim/fast_out_extra_slow_in.xml b/fragment/fragment/src/main/res/anim/fragment_fast_out_extra_slow_in.xml
similarity index 100%
rename from fragment/fragment/src/main/res/anim/fast_out_extra_slow_in.xml
rename to fragment/fragment/src/main/res/anim/fragment_fast_out_extra_slow_in.xml
diff --git a/fragment/fragment/src/main/res/anim/fragment_open_enter.xml b/fragment/fragment/src/main/res/anim/fragment_open_enter.xml
index e667dc7..e74d0e2 100644
--- a/fragment/fragment/src/main/res/anim/fragment_open_enter.xml
+++ b/fragment/fragment/src/main/res/anim/fragment_open_enter.xml
@@ -38,7 +38,7 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:pathData="@anim/fast_out_extra_slow_in"
+        android:pathData="@anim/fragment_fast_out_extra_slow_in"
         android:duration="400"
         tools:targetApi="lollipop" />
 </set>
\ No newline at end of file
diff --git a/fragment/fragment/src/main/res/anim/fragment_open_exit.xml b/fragment/fragment/src/main/res/anim/fragment_open_exit.xml
index 4ea1ea7..ac1a760 100644
--- a/fragment/fragment/src/main/res/anim/fragment_open_exit.xml
+++ b/fragment/fragment/src/main/res/anim/fragment_open_exit.xml
@@ -38,6 +38,6 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@anim/fast_out_extra_slow_in"
+        android:interpolator="@anim/fragment_fast_out_extra_slow_in"
         android:duration="400"/>
 </set>
\ No newline at end of file
diff --git a/benchmark/src/androidTest/AndroidManifest.xml b/fragment/fragment/src/main/res/values/ids.xml
similarity index 65%
copy from benchmark/src/androidTest/AndroidManifest.xml
copy to fragment/fragment/src/main/res/values/ids.xml
index f5ec776..db4ce6e 100644
--- a/benchmark/src/androidTest/AndroidManifest.xml
+++ b/fragment/fragment/src/main/res/values/ids.xml
@@ -14,13 +14,7 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<manifest
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="androidx.benchmark.test">
 
-    <application
-        android:name="androidx.benchmark.ArgumentInjectingApplication">
-        <activity android:name="android.app.Activity"/>
-    </application>
-</manifest>
\ No newline at end of file
+<resources>
+        <item type="id" name="fragment_container_view_tag" />
+</resources>
\ No newline at end of file
diff --git a/graphics/drawable/static/build.gradle b/graphics/drawable/static/build.gradle
index a3e453a..f5e1ba4 100644
--- a/graphics/drawable/static/build.gradle
+++ b/graphics/drawable/static/build.gradle
@@ -10,7 +10,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.1.0")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/gridlayout/build.gradle b/gridlayout/build.gradle
index efb7375..58ab795 100644
--- a/gridlayout/build.gradle
+++ b/gridlayout/build.gradle
@@ -10,7 +10,7 @@
 
 dependencies {
     api(ANDROIDX_ANNOTATION)
-    implementation("androidx.core:core:1.1.0-rc01")
+    implementation("androidx.core:core:1.1.0")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/jetifier/jetifier/migration.config b/jetifier/jetifier/migration.config
index 03e7d2a..16ac67b 100644
--- a/jetifier/jetifier/migration.config
+++ b/jetifier/jetifier/migration.config
@@ -614,6 +614,10 @@
       "to": "ignore"
     },
     {
+      "from": "androidx/benchmark/(.*)",
+      "to": "ignore"
+    },
+    {
       "from": "androidx/camera/(.*)",
       "to": "ignore"
     },
@@ -972,6 +976,10 @@
       "to": "androidx/sharetarget"
     },
     {
+      "from": "androidx/benchmark",
+      "to": "androidx/benchmark"
+    },
+    {
       "from": "androidx/camera",
       "to": "androidx/camera"
     },
@@ -3023,6 +3031,30 @@
     },
     {
       "from": {
+        "groupId": "androidx.benchmark",
+        "artifactId": "benchmark-common",
+        "version": "{newBenchmarkVersion}"
+      },
+      "to": {
+        "groupId": "androidx.benchmark",
+        "artifactId": "benchmark-common",
+        "version": "{newBenchmarkVersion}"
+      }
+    },
+    {
+      "from": {
+        "groupId": "androidx.benchmark",
+        "artifactId": "benchmark-junit4",
+        "version": "{newBenchmarkVersion}"
+      },
+      "to": {
+        "groupId": "androidx.benchmark",
+        "artifactId": "benchmark-junit4",
+        "version": "{newBenchmarkVersion}"
+      }
+    },
+    {
+      "from": {
         "groupId": "androidx.camera",
         "artifactId": "camera-core",
         "version": "{newCameraVersion}"
@@ -3133,6 +3165,7 @@
       "newBiometricVersion": "1.0.0-alpha03",
       "newDataBindingVersion": "undefined",
       "newWorkManagerVersion": "2.0.0",
+      "newBenchmarkVersion": "1.0.0-alpha04",
       "newCameraVersion": "1.0.0-alpha01"
     }
   },
diff --git a/leanback/api/1.1.0-alpha03.ignore b/leanback/api/1.1.0-alpha03.ignore
index 2a59565..cf7fad9 100644
--- a/leanback/api/1.1.0-alpha03.ignore
+++ b/leanback/api/1.1.0-alpha03.ignore
@@ -1,3 +1,7 @@
 // Baseline format: 1.0
 ChangedStatic: androidx.leanback.widget.ItemBridgeAdapter.ViewHolder:
     Class androidx.leanback.widget.ItemBridgeAdapter.ViewHolder changed 'static' qualifier
+
+
+RemovedDeprecatedMethod: androidx.leanback.app.RowsFragment#onCreate(android.os.Bundle):
+    Removed deprecated method androidx.leanback.app.RowsFragment.onCreate(android.os.Bundle)
diff --git a/leanback/api/restricted_1.1.0-alpha03.ignore b/leanback/api/restricted_1.1.0-alpha03.ignore
index 5dbae58..c94da04 100644
--- a/leanback/api/restricted_1.1.0-alpha03.ignore
+++ b/leanback/api/restricted_1.1.0-alpha03.ignore
@@ -3,5 +3,9 @@
     Class androidx.leanback.widget.ItemBridgeAdapter.ViewHolder changed 'static' qualifier
 
 
+RemovedDeprecatedMethod: androidx.leanback.app.RowsFragment#onCreate(android.os.Bundle):
+    Removed deprecated method androidx.leanback.app.RowsFragment.onCreate(android.os.Bundle)
+
+
 RemovedMethod: androidx.leanback.widget.picker.DatePicker#updateDate(int, int, int, boolean):
     Removed method androidx.leanback.widget.picker.DatePicker.updateDate(int,int,int,boolean)
diff --git a/leanback/build.gradle b/leanback/build.gradle
index 7588656..df32fcf 100644
--- a/leanback/build.gradle
+++ b/leanback/build.gradle
@@ -11,7 +11,7 @@
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
     api("androidx.interpolator:interpolator:1.0.0")
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.media:media:1.0.0")
     api("androidx.fragment:fragment:1.0.0")
diff --git a/legacy/core-utils/build.gradle b/legacy/core-utils/build.gradle
index 7a331a9..c4fed24 100644
--- a/legacy/core-utils/build.gradle
+++ b/legacy/core-utils/build.gradle
@@ -9,7 +9,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     api(project(":documentfile"))
     api(project(":loader:loader"))
     api(project(":localbroadcastmanager"))
diff --git a/lifecycle/lifecycle-extensions/api/2.2.0-alpha03.txt b/lifecycle/lifecycle-extensions/api/2.2.0-alpha03.txt
index c85f1ab..273ffbf 100644
--- a/lifecycle/lifecycle-extensions/api/2.2.0-alpha03.txt
+++ b/lifecycle/lifecycle-extensions/api/2.2.0-alpha03.txt
@@ -1,12 +1,12 @@
 // Signature format: 3.0
 package androidx.lifecycle {
 
-  public class ViewModelProviders {
+  @Deprecated public class ViewModelProviders {
     ctor @Deprecated public ViewModelProviders();
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
   }
 
   @Deprecated public static class ViewModelProviders.DefaultFactory extends androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory {
diff --git a/lifecycle/lifecycle-extensions/api/current.txt b/lifecycle/lifecycle-extensions/api/current.txt
index c85f1ab..273ffbf 100644
--- a/lifecycle/lifecycle-extensions/api/current.txt
+++ b/lifecycle/lifecycle-extensions/api/current.txt
@@ -1,12 +1,12 @@
 // Signature format: 3.0
 package androidx.lifecycle {
 
-  public class ViewModelProviders {
+  @Deprecated public class ViewModelProviders {
     ctor @Deprecated public ViewModelProviders();
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
   }
 
   @Deprecated public static class ViewModelProviders.DefaultFactory extends androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory {
diff --git a/lifecycle/lifecycle-extensions/api/restricted_2.2.0-alpha03.txt b/lifecycle/lifecycle-extensions/api/restricted_2.2.0-alpha03.txt
index c85f1ab..273ffbf 100644
--- a/lifecycle/lifecycle-extensions/api/restricted_2.2.0-alpha03.txt
+++ b/lifecycle/lifecycle-extensions/api/restricted_2.2.0-alpha03.txt
@@ -1,12 +1,12 @@
 // Signature format: 3.0
 package androidx.lifecycle {
 
-  public class ViewModelProviders {
+  @Deprecated public class ViewModelProviders {
     ctor @Deprecated public ViewModelProviders();
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
   }
 
   @Deprecated public static class ViewModelProviders.DefaultFactory extends androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory {
diff --git a/lifecycle/lifecycle-extensions/api/restricted_current.txt b/lifecycle/lifecycle-extensions/api/restricted_current.txt
index c85f1ab..273ffbf 100644
--- a/lifecycle/lifecycle-extensions/api/restricted_current.txt
+++ b/lifecycle/lifecycle-extensions/api/restricted_current.txt
@@ -1,12 +1,12 @@
 // Signature format: 3.0
 package androidx.lifecycle {
 
-  public class ViewModelProviders {
+  @Deprecated public class ViewModelProviders {
     ctor @Deprecated public ViewModelProviders();
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
-    method @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.Fragment, androidx.lifecycle.ViewModelProvider.Factory?);
+    method @Deprecated @MainThread public static androidx.lifecycle.ViewModelProvider of(androidx.fragment.app.FragmentActivity, androidx.lifecycle.ViewModelProvider.Factory?);
   }
 
   @Deprecated public static class ViewModelProviders.DefaultFactory extends androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory {
diff --git a/lifecycle/lifecycle-extensions/src/androidTest/java/androidx/lifecycle/ViewModelProvidersFragmentTest.kt b/lifecycle/lifecycle-extensions/src/androidTest/java/androidx/lifecycle/ViewModelProvidersFragmentTest.kt
index 696c0c9..9cfd0c9 100644
--- a/lifecycle/lifecycle-extensions/src/androidTest/java/androidx/lifecycle/ViewModelProvidersFragmentTest.kt
+++ b/lifecycle/lifecycle-extensions/src/androidTest/java/androidx/lifecycle/ViewModelProvidersFragmentTest.kt
@@ -25,6 +25,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@Suppress("DEPRECATION")
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 class ViewModelProvidersFragmentTest {
diff --git a/lifecycle/lifecycle-extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java b/lifecycle/lifecycle-extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java
index 0b83b92..2bf899e 100644
--- a/lifecycle/lifecycle-extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java
+++ b/lifecycle/lifecycle-extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java
@@ -16,7 +16,6 @@
 
 package androidx.lifecycle;
 
-import android.app.Activity;
 import android.app.Application;
 
 import androidx.annotation.MainThread;
@@ -28,7 +27,10 @@
 
 /**
  * Utilities methods for {@link ViewModelStore} class.
+ *
+ * @deprecated Use the constructors for {@link ViewModelProvider} directly.
  */
+@Deprecated
 public class ViewModelProviders {
 
     /**
@@ -38,51 +40,44 @@
     public ViewModelProviders() {
     }
 
-    private static Application checkApplication(Activity activity) {
-        Application application = activity.getApplication();
-        if (application == null) {
-            throw new IllegalStateException("Your activity/fragment is not yet attached to "
-                    + "Application. You can't request ViewModel before onCreate call.");
-        }
-        return application;
-    }
-
-    private static Activity checkActivity(Fragment fragment) {
-        Activity activity = fragment.getActivity();
-        if (activity == null) {
-            throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
-        }
-        return activity;
-    }
-
     /**
      * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given
      * {@code fragment} is alive. More detailed explanation is in {@link ViewModel}.
      * <p>
-     * It uses {@link ViewModelProvider.AndroidViewModelFactory} to instantiate new ViewModels.
+     * It uses the {@link Fragment#getDefaultViewModelProviderFactory() default factory}
+     * to instantiate new ViewModels.
      *
      * @param fragment a fragment, in whose scope ViewModels should be retained
      * @return a ViewModelProvider instance
+     * @deprecated Use the 'by viewModels()' Kotlin property delegate or
+     * {@link ViewModelProvider#ViewModelProvider(ViewModelStoreOwner)},
+     * passing in the fragment.
      */
+    @Deprecated
     @NonNull
     @MainThread
     public static ViewModelProvider of(@NonNull Fragment fragment) {
-        return of(fragment, null);
+        return new ViewModelProvider(fragment);
     }
 
     /**
      * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given Activity
      * is alive. More detailed explanation is in {@link ViewModel}.
      * <p>
-     * It uses {@link ViewModelProvider.AndroidViewModelFactory} to instantiate new ViewModels.
+     * It uses the {@link FragmentActivity#getDefaultViewModelProviderFactory() default factory}
+     * to instantiate new ViewModels.
      *
      * @param activity an activity, in whose scope ViewModels should be retained
      * @return a ViewModelProvider instance
+     * @deprecated Use the 'by viewModels()' Kotlin property delegate or
+     * {@link ViewModelProvider#ViewModelProvider(ViewModelStoreOwner)},
+     * passing in the activity.
      */
+    @Deprecated
     @NonNull
     @MainThread
     public static ViewModelProvider of(@NonNull FragmentActivity activity) {
-        return of(activity, null);
+        return new ViewModelProvider(activity);
     }
 
     /**
@@ -94,13 +89,16 @@
      * @param fragment a fragment, in whose scope ViewModels should be retained
      * @param factory  a {@code Factory} to instantiate new ViewModels
      * @return a ViewModelProvider instance
+     * @deprecated Use the 'by viewModels()' Kotlin property delegate or
+     * {@link ViewModelProvider#ViewModelProvider(ViewModelStoreOwner, Factory)},
+     * passing in the fragment and factory.
      */
+    @Deprecated
     @NonNull
     @MainThread
     public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
-        Application application = checkApplication(checkActivity(fragment));
         if (factory == null) {
-            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
+            factory = fragment.getDefaultViewModelProviderFactory();
         }
         return new ViewModelProvider(fragment.getViewModelStore(), factory);
     }
@@ -114,14 +112,17 @@
      * @param activity an activity, in whose scope ViewModels should be retained
      * @param factory  a {@code Factory} to instantiate new ViewModels
      * @return a ViewModelProvider instance
+     * @deprecated Use the 'by viewModels()' Kotlin property delegate or
+     * {@link ViewModelProvider#ViewModelProvider(ViewModelStoreOwner, Factory)},
+     * passing in the activity and factory.
      */
+    @Deprecated
     @NonNull
     @MainThread
     public static ViewModelProvider of(@NonNull FragmentActivity activity,
             @Nullable Factory factory) {
-        Application application = checkApplication(activity);
         if (factory == null) {
-            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
+            factory = activity.getDefaultViewModelProviderFactory();
         }
         return new ViewModelProvider(activity.getViewModelStore(), factory);
     }
diff --git a/lifecycle/lifecycle-extensions/src/test/java/androidx/lifecycle/ViewModelProvidersTest.java b/lifecycle/lifecycle-extensions/src/test/java/androidx/lifecycle/ViewModelProvidersTest.java
index e21f5c1..1dc35ae 100644
--- a/lifecycle/lifecycle-extensions/src/test/java/androidx/lifecycle/ViewModelProvidersTest.java
+++ b/lifecycle/lifecycle-extensions/src/test/java/androidx/lifecycle/ViewModelProvidersTest.java
@@ -23,6 +23,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+@SuppressWarnings("deprecation")
 @RunWith(JUnit4.class)
 public class ViewModelProvidersTest {
 
diff --git a/lifecycle/lifecycle-livedata/api/2.1.0-beta02.txt b/lifecycle/lifecycle-livedata/api/2.1.0-beta02.txt
index 2f5616b..b9a2837 100644
--- a/lifecycle/lifecycle-livedata/api/2.1.0-beta02.txt
+++ b/lifecycle/lifecycle-livedata/api/2.1.0-beta02.txt
@@ -3,14 +3,14 @@
 
   public class MediatorLiveData<T> extends androidx.lifecycle.MutableLiveData<T> {
     ctor public MediatorLiveData();
-    method @MainThread public <S> void addSource(androidx.lifecycle.LiveData<S>, androidx.lifecycle.Observer<? super S>);
-    method @MainThread public <S> void removeSource(androidx.lifecycle.LiveData<S>);
+    method @MainThread public <S> void addSource(androidx.lifecycle.LiveData<S!>, androidx.lifecycle.Observer<? super S>);
+    method @MainThread public <S> void removeSource(androidx.lifecycle.LiveData<S!>);
   }
 
   public class Transformations {
-    method @MainThread public static <X> androidx.lifecycle.LiveData<X> distinctUntilChanged(androidx.lifecycle.LiveData<X>);
-    method @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> map(androidx.lifecycle.LiveData<X>, androidx.arch.core.util.Function<X,Y>);
-    method @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y> switchMap(androidx.lifecycle.LiveData<X>, androidx.arch.core.util.Function<X,androidx.lifecycle.LiveData<Y>>);
+    method @MainThread public static <X> androidx.lifecycle.LiveData<X!> distinctUntilChanged(androidx.lifecycle.LiveData<X!>);
+    method @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y!> map(androidx.lifecycle.LiveData<X!>, androidx.arch.core.util.Function<X!,Y!>);
+    method @MainThread public static <X, Y> androidx.lifecycle.LiveData<Y!> switchMap(androidx.lifecycle.LiveData<X!>, androidx.arch.core.util.Function<X!,androidx.lifecycle.LiveData<Y!>!>);
   }
 
 }
diff --git a/lifecycle/lifecycle-reactivestreams/api/2.1.0-beta02.txt b/lifecycle/lifecycle-reactivestreams/api/2.1.0-beta02.txt
index f3d107a..518fc95 100644
--- a/lifecycle/lifecycle-reactivestreams/api/2.1.0-beta02.txt
+++ b/lifecycle/lifecycle-reactivestreams/api/2.1.0-beta02.txt
@@ -2,8 +2,8 @@
 package androidx.lifecycle {
 
   public final class LiveDataReactiveStreams {
-    method public static <T> androidx.lifecycle.LiveData<T> fromPublisher(org.reactivestreams.Publisher<T>);
-    method public static <T> org.reactivestreams.Publisher<T> toPublisher(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.LiveData<T>);
+    method public static <T> androidx.lifecycle.LiveData<T!> fromPublisher(org.reactivestreams.Publisher<T!>);
+    method public static <T> org.reactivestreams.Publisher<T!> toPublisher(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.LiveData<T!>);
   }
 
 }
diff --git a/lifecycle/lifecycle-viewmodel-ktx/api/2.2.0-alpha03.ignore b/lifecycle/lifecycle-viewmodel-ktx/api/2.2.0-alpha03.ignore
deleted file mode 100644
index fd00442..0000000
--- a/lifecycle/lifecycle-viewmodel-ktx/api/2.2.0-alpha03.ignore
+++ /dev/null
@@ -1,3 +0,0 @@
-// Baseline format: 1.0
-ChangedType: androidx.lifecycle.ViewModelProviderKt#get(androidx.lifecycle.ViewModelProvider):
-    Method androidx.lifecycle.ViewModelProviderKt.get has changed return type from java.lang.VM to VM
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/1.0.0-alpha03.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/1.0.0-alpha03.txt
index 53339c9..8bd77f2 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/api/1.0.0-alpha03.txt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/1.0.0-alpha03.txt
@@ -20,10 +20,7 @@
   }
 
   public final class SavedStateViewModelFactory extends androidx.lifecycle.AbstractSavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment);
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment, android.os.Bundle?);
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity);
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity, android.os.Bundle?);
+    ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner);
     ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner, android.os.Bundle?);
     method protected <T extends androidx.lifecycle.ViewModel> T create(String, Class<T!>, androidx.lifecycle.SavedStateHandle);
   }
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/current.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/current.txt
index 53339c9..8bd77f2 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/api/current.txt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/current.txt
@@ -20,10 +20,7 @@
   }
 
   public final class SavedStateViewModelFactory extends androidx.lifecycle.AbstractSavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment);
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment, android.os.Bundle?);
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity);
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity, android.os.Bundle?);
+    ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner);
     ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner, android.os.Bundle?);
     method protected <T extends androidx.lifecycle.ViewModel> T create(String, Class<T!>, androidx.lifecycle.SavedStateHandle);
   }
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_1.0.0-alpha03.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_1.0.0-alpha03.txt
index b023830b..efd5ff3 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_1.0.0-alpha03.txt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_1.0.0-alpha03.txt
@@ -21,10 +21,7 @@
   }
 
   public final class SavedStateViewModelFactory extends androidx.lifecycle.AbstractSavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment);
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment, android.os.Bundle?);
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity);
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity, android.os.Bundle?);
+    ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner);
     ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner, android.os.Bundle?);
     method protected <T extends androidx.lifecycle.ViewModel> T create(String, Class<T!>, androidx.lifecycle.SavedStateHandle);
   }
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_current.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_current.txt
index b023830b..efd5ff3 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_current.txt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_current.txt
@@ -21,10 +21,7 @@
   }
 
   public final class SavedStateViewModelFactory extends androidx.lifecycle.AbstractSavedStateViewModelFactory implements androidx.lifecycle.ViewModelProvider.Factory {
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment);
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.Fragment, android.os.Bundle?);
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity);
-    ctor public SavedStateViewModelFactory(androidx.fragment.app.FragmentActivity, android.os.Bundle?);
+    ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner);
     ctor public SavedStateViewModelFactory(android.app.Application, androidx.savedstate.SavedStateRegistryOwner, android.os.Bundle?);
     method protected <T extends androidx.lifecycle.ViewModel> T create(String, Class<T!>, androidx.lifecycle.SavedStateHandle);
   }
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/build.gradle b/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
index 2d58931..ad09591 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
@@ -38,10 +38,9 @@
     api(project(":lifecycle:lifecycle-livedata-core"))
     api(project(":lifecycle:lifecycle-viewmodel"))
 
-    api project(":fragment:fragment"), {
-        exclude group: 'androidx.lifecycle', module: 'lifecycle-livedata-core'
+    androidTestImplementation project(":fragment:fragment"), {
+        exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-savedstate'
     }
-
     androidTestImplementation(TRUTH)
     androidTestImplementation(KOTLIN_STDLIB)
     androidTestImplementation(ESPRESSO_CORE)
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateFactoryTest.kt b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateFactoryTest.kt
index 0277b01..2da4e17 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateFactoryTest.kt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/SavedStateFactoryTest.kt
@@ -41,8 +41,9 @@
 
     @Test
     fun testCreateAndroidVM() {
-        val savedStateVMFactory =
-            SavedStateViewModelFactory(activityRule.activity)
+        val savedStateVMFactory = SavedStateViewModelFactory(
+            activityRule.activity.application,
+            activityRule.activity)
         val vm = ViewModelProvider(ViewModelStore(), savedStateVMFactory)
         assertThat(vm.get(MyAndroidViewModel::class.java).handle).isNotNull()
         assertThat(vm.get(MyViewModel::class.java).handle).isNotNull()
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/ViewModelsWithStateTests.java b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/ViewModelsWithStateTests.java
index 5b2b458..8f0a31d 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/ViewModelsWithStateTests.java
+++ b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/viewmodel/savedstate/ViewModelsWithStateTests.java
@@ -143,14 +143,17 @@
     private ViewModelProvider vmProvider(FakingSavedStateActivity activity) {
         if (FRAGMENT_MODE.equals(mode)) {
             Fragment fragment = activity.getFragment();
-            return new ViewModelProvider(fragment, new SavedStateViewModelFactory(fragment));
+            return new ViewModelProvider(fragment, new SavedStateViewModelFactory(
+                    fragment.requireActivity().getApplication(), fragment));
         }
-        return new ViewModelProvider(activity, new SavedStateViewModelFactory(activity));
+        return new ViewModelProvider(activity, new SavedStateViewModelFactory(
+                activity.getApplication(), activity));
     }
 
     // copy copy copy paste
     @SuppressWarnings("unchecked")
-    private static <T extends Activity> T recreateActivity(final T activity, ActivityTestRule rule)
+    private static <T extends Activity> T recreateActivity(final T activity,
+            ActivityTestRule<?> rule)
             throws Throwable {
         Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
                 activity.getClass().getCanonicalName(), null, false);
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateViewModelFactory.java b/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateViewModelFactory.java
index a4c48dc..59a3039 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateViewModelFactory.java
+++ b/lifecycle/lifecycle-viewmodel-savedstate/src/main/java/androidx/lifecycle/SavedStateViewModelFactory.java
@@ -16,14 +16,12 @@
 
 package androidx.lifecycle;
 
-import android.app.Activity;
+import android.annotation.SuppressLint;
 import android.app.Application;
 import android.os.Bundle;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
 import androidx.savedstate.SavedStateRegistryOwner;
 
 import java.lang.reflect.Constructor;
@@ -33,8 +31,7 @@
 /**
  * {@link androidx.lifecycle.ViewModelProvider.Factory} that can create ViewModels accessing and
  * contributing to a saved state via {@link SavedStateHandle} received in a constructor.
- * If {@code defaultArgs} bundle was passed in {@link #SavedStateViewModelFactory(Fragment, Bundle)}
- * or {@link #SavedStateViewModelFactory(FragmentActivity, Bundle)}, it will provide default
+ * If {@code defaultArgs} bundle was passed into the constructor, it will provide default
  * values in {@code SavedStateHandle}.
  * <p>
  * If ViewModel is instance of {@link androidx.lifecycle.AndroidViewModel}, it looks for a
@@ -49,55 +46,15 @@
      * Creates {@link SavedStateViewModelFactory}.
      * <p>
      * {@link androidx.lifecycle.ViewModel} created with this factory can access to saved state
-     * scoped to the given {@code fragment}.
-     *
-     * @param fragment scope of this fragment will be used for state saving
-     */
-    public SavedStateViewModelFactory(@NonNull Fragment fragment) {
-        this(fragment, null);
-    }
-
-    /**
-     * Creates {@link SavedStateViewModelFactory}.
-     * <p>
-     * {@link androidx.lifecycle.ViewModel} created with this factory can access to saved state
-     * scoped to the given {@code fragment}.
-     *
-     * @param fragment scope of this fragment will be used for state saving
-     * @param defaultArgs values from this {@code Bundle} will be used as defaults by
-     * {@link SavedStateHandle} if there is no previously saved state or previously saved state
-     * miss a value by such key.
-     */
-    public SavedStateViewModelFactory(@NonNull Fragment fragment, @Nullable Bundle defaultArgs) {
-        this(checkApplication(checkActivity(fragment)), fragment, defaultArgs);
-    }
-
-    /**
-     * Creates {@link SavedStateViewModelFactory}.
-     * <p>
-     * {@link androidx.lifecycle.ViewModel} created with this factory can access to saved state
      * scoped to the given {@code activity}.
      *
-     * @param activity scope of this activity will be used for state saving
+     * @param application an application
+     * @param owner {@link SavedStateRegistryOwner} that will provide restored state for created
+     * {@link androidx.lifecycle.ViewModel ViewModels}
      */
-    public SavedStateViewModelFactory(@NonNull FragmentActivity activity) {
-        this(activity, null);
-    }
-
-    /**
-     * Creates {@link SavedStateViewModelFactory}.
-     * <p>
-     * {@link androidx.lifecycle.ViewModel} created with this factory can access to saved state
-     * scoped to the given {@code activity}.
-     *
-     * @param activity scope of this activity will be used for state saving
-     * @param defaultArgs values from this {@code Bundle} will be used as defaults by
-     * {@link SavedStateHandle} if there is no previously saved state or previously saved state
-     * misses a value by such key.
-     */
-    public SavedStateViewModelFactory(@NonNull FragmentActivity activity,
-            @Nullable Bundle defaultArgs) {
-        this(checkApplication(activity), activity, defaultArgs);
+    public SavedStateViewModelFactory(@NonNull Application application,
+            @NonNull SavedStateRegistryOwner owner) {
+        this(application, owner, null);
     }
 
     /**
@@ -113,6 +70,7 @@
      * {@link SavedStateHandle} if there is no previously saved state or previously saved state
      * misses a value by such key.
      */
+    @SuppressLint("LambdaLast")
     public SavedStateViewModelFactory(@NonNull Application application,
             @NonNull SavedStateRegistryOwner owner,
             @Nullable Bundle defaultArgs) {
@@ -166,23 +124,4 @@
         }
         return null;
     }
-
-    private static Application checkApplication(Activity activity) {
-        Application application = activity.getApplication();
-        if (application == null) {
-            throw new IllegalStateException("Your activity/fragment is not yet attached to "
-                    + "Application. You can't request ViewModelsWithStateFactory "
-                    + "before onCreate call.");
-        }
-        return application;
-    }
-
-    private static Activity checkActivity(Fragment fragment) {
-        Activity activity = fragment.getActivity();
-        if (activity == null) {
-            throw new IllegalStateException("Can't create ViewModelsWithStateFactory"
-                    + " for detached fragment");
-        }
-        return activity;
-    }
 }
diff --git a/lifecycle/lifecycle-viewmodel/api/2.2.0-alpha03.txt b/lifecycle/lifecycle-viewmodel/api/2.2.0-alpha03.txt
index 1c69a2a..07a8cb5 100644
--- a/lifecycle/lifecycle-viewmodel/api/2.2.0-alpha03.txt
+++ b/lifecycle/lifecycle-viewmodel/api/2.2.0-alpha03.txt
@@ -6,12 +6,17 @@
     method public <T extends android.app.Application> T getApplication();
   }
 
+  public interface HasDefaultViewModelProviderFactory {
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+  }
+
   public abstract class ViewModel {
     ctor public ViewModel();
     method protected void onCleared();
   }
 
   public class ViewModelProvider {
+    ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner);
     ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner, androidx.lifecycle.ViewModelProvider.Factory);
     ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore, androidx.lifecycle.ViewModelProvider.Factory);
     method @MainThread public <T extends androidx.lifecycle.ViewModel> T get(Class<T!>);
diff --git a/lifecycle/lifecycle-viewmodel/api/current.txt b/lifecycle/lifecycle-viewmodel/api/current.txt
index 1c69a2a..07a8cb5 100644
--- a/lifecycle/lifecycle-viewmodel/api/current.txt
+++ b/lifecycle/lifecycle-viewmodel/api/current.txt
@@ -6,12 +6,17 @@
     method public <T extends android.app.Application> T getApplication();
   }
 
+  public interface HasDefaultViewModelProviderFactory {
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+  }
+
   public abstract class ViewModel {
     ctor public ViewModel();
     method protected void onCleared();
   }
 
   public class ViewModelProvider {
+    ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner);
     ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner, androidx.lifecycle.ViewModelProvider.Factory);
     ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore, androidx.lifecycle.ViewModelProvider.Factory);
     method @MainThread public <T extends androidx.lifecycle.ViewModel> T get(Class<T!>);
diff --git a/lifecycle/lifecycle-viewmodel/api/restricted_2.2.0-alpha03.txt b/lifecycle/lifecycle-viewmodel/api/restricted_2.2.0-alpha03.txt
index 1c69a2a..07a8cb5 100644
--- a/lifecycle/lifecycle-viewmodel/api/restricted_2.2.0-alpha03.txt
+++ b/lifecycle/lifecycle-viewmodel/api/restricted_2.2.0-alpha03.txt
@@ -6,12 +6,17 @@
     method public <T extends android.app.Application> T getApplication();
   }
 
+  public interface HasDefaultViewModelProviderFactory {
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+  }
+
   public abstract class ViewModel {
     ctor public ViewModel();
     method protected void onCleared();
   }
 
   public class ViewModelProvider {
+    ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner);
     ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner, androidx.lifecycle.ViewModelProvider.Factory);
     ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore, androidx.lifecycle.ViewModelProvider.Factory);
     method @MainThread public <T extends androidx.lifecycle.ViewModel> T get(Class<T!>);
diff --git a/lifecycle/lifecycle-viewmodel/api/restricted_current.txt b/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
index 1c69a2a..07a8cb5 100644
--- a/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
+++ b/lifecycle/lifecycle-viewmodel/api/restricted_current.txt
@@ -6,12 +6,17 @@
     method public <T extends android.app.Application> T getApplication();
   }
 
+  public interface HasDefaultViewModelProviderFactory {
+    method public androidx.lifecycle.ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+  }
+
   public abstract class ViewModel {
     ctor public ViewModel();
     method protected void onCleared();
   }
 
   public class ViewModelProvider {
+    ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner);
     ctor public ViewModelProvider(androidx.lifecycle.ViewModelStoreOwner, androidx.lifecycle.ViewModelProvider.Factory);
     ctor public ViewModelProvider(androidx.lifecycle.ViewModelStore, androidx.lifecycle.ViewModelProvider.Factory);
     method @MainThread public <T extends androidx.lifecycle.ViewModel> T get(Class<T!>);
diff --git a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/HasDefaultViewModelProviderFactory.java b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/HasDefaultViewModelProviderFactory.java
new file mode 100644
index 0000000..1db6658
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/HasDefaultViewModelProviderFactory.java
@@ -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.
+ */
+
+package androidx.lifecycle;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Interface that marks a {@link ViewModelStoreOwner} as having a default
+ * {@link ViewModelProvider.Factory} for use with
+ * {@link ViewModelProvider#ViewModelProvider(ViewModelStoreOwner)}.
+ */
+public interface HasDefaultViewModelProviderFactory {
+    /**
+     * Returns the default {@link ViewModelProvider.Factory} that should be
+     * used when no custom {@code Factory} is provided to the
+     * {@link ViewModelProvider} constructors.
+     *
+     * @return a {@code ViewModelProvider.Factory}
+     */
+    @NonNull
+    ViewModelProvider.Factory getDefaultViewModelProviderFactory();
+}
diff --git a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModel.java b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModel.java
index 0709327..259430f 100644
--- a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModel.java
+++ b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModel.java
@@ -54,7 +54,7 @@
  *     protected void onCreate(Bundle savedInstanceState) {
  *         super.onCreate(savedInstanceState);
  *         setContentView(R.layout.user_activity_layout);
- *         final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
+ *         final UserModel viewModel = new ViewModelProvider(this).get(UserModel.class);
  *         viewModel.userLiveData.observer(this, new Observer<User>() {
  *            {@literal @}Override
  *             public void onChanged(@Nullable User data) {
@@ -99,7 +99,7 @@
  * <pre>
  * public class MyFragment extends Fragment {
  *     public void onStart() {
- *         UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);
+ *         UserModel userModel = new ViewModelProvider(requireActivity()).get(UserModel.class);
  *     }
  * }
  * </pre>
diff --git a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.java b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.java
index 56d9f1b..e66da15 100644
--- a/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.java
+++ b/lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModelProvider.java
@@ -27,7 +27,7 @@
  * An utility class that provides {@code ViewModels} for a scope.
  * <p>
  * Default {@code ViewModelProvider} for an {@code Activity} or a {@code Fragment} can be obtained
- * from {@link androidx.lifecycle.ViewModelProviders} class.
+ * by passing it to {@link ViewModelProvider#ViewModelProvider(ViewModelStoreOwner)}.
  */
 @SuppressWarnings("WeakerAccess")
 public class ViewModelProvider {
@@ -82,6 +82,21 @@
     private final ViewModelStore mViewModelStore;
 
     /**
+     * Creates {@code ViewModelProvider}. This will create {@code ViewModels}
+     * and retain them in a store of the given {@code ViewModelStoreOwner}.
+     * <p>
+     * This method will use the
+     * {@link HasDefaultViewModelProviderFactory#getDefaultViewModelProviderFactory() default factory}
+     * if the owner implements {@link HasDefaultViewModelProviderFactory}. Otherwise, a
+     * {@link NewInstanceFactory} will be used.
+     */
+    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
+        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
+                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
+                : NewInstanceFactory.getInstance());
+    }
+
+    /**
      * Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
      * {@code Factory} and retain them in a store of the given {@code ViewModelStoreOwner}.
      *
@@ -172,6 +187,21 @@
      */
     public static class NewInstanceFactory implements Factory {
 
+        private static NewInstanceFactory sInstance;
+
+        /**
+         * Retrieve a singleton instance of NewInstanceFactory.
+         *
+         * @return A valid {@link NewInstanceFactory}
+         */
+        @NonNull
+        static NewInstanceFactory getInstance() {
+            if (sInstance == null) {
+                sInstance = new NewInstanceFactory();
+            }
+            return sInstance;
+        }
+
         @SuppressWarnings("ClassNewInstance")
         @NonNull
         @Override
diff --git a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelProviderTest.java b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelProviderTest.java
index dd9470f..0d5783c 100644
--- a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelProviderTest.java
+++ b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelProviderTest.java
@@ -85,6 +85,17 @@
     }
 
     @Test
+    public void testCustomDefaultFactory() {
+        final ViewModelStore store = new ViewModelStore();
+        final CountingFactory factory = new CountingFactory();
+        ViewModelStoreOwnerWithFactory owner = new ViewModelStoreOwnerWithFactory(store, factory);
+        ViewModelProvider provider = new ViewModelProvider(owner);
+        ViewModel1 viewModel = provider.get(ViewModel1.class);
+        assertThat(viewModel, is(provider.get(ViewModel1.class)));
+        assertThat(factory.mCalled, is(1));
+    }
+
+    @Test
     public void testKeyedFactory() {
         final ViewModelStore store = new ViewModelStore();
         ViewModelStoreOwner owner = new ViewModelStoreOwner() {
@@ -107,6 +118,30 @@
         provider.get("customkey", ViewModel1.class);
     }
 
+    public static class ViewModelStoreOwnerWithFactory implements
+            ViewModelStoreOwner, HasDefaultViewModelProviderFactory {
+        private final ViewModelStore mStore;
+        private final ViewModelProvider.Factory mFactory;
+
+        ViewModelStoreOwnerWithFactory(@NonNull ViewModelStore store,
+                @NonNull ViewModelProvider.Factory factory) {
+            mStore = store;
+            mFactory = factory;
+        }
+
+        @NonNull
+        @Override
+        public ViewModelStore getViewModelStore() {
+            return mStore;
+        }
+
+        @NonNull
+        @Override
+        public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
+            return mFactory;
+        }
+    }
+
     public static class ViewModel1 extends ViewModel {
         boolean mCleared;
 
@@ -118,4 +153,15 @@
 
     public static class ViewModel2 extends ViewModel {
     }
+
+    public static class CountingFactory extends NewInstanceFactory {
+        int mCalled = 0;
+
+        @Override
+        @NonNull
+        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
+            mCalled++;
+            return super.create(modelClass);
+        }
+    }
 }
diff --git a/loader/loader-ktx/src/androidTest/java/androidx/loader/app/LoaderManagerTest.kt b/loader/loader-ktx/src/androidTest/java/androidx/loader/app/LoaderManagerTest.kt
index 3a92843..ef302aa 100644
--- a/loader/loader-ktx/src/androidTest/java/androidx/loader/app/LoaderManagerTest.kt
+++ b/loader/loader-ktx/src/androidTest/java/androidx/loader/app/LoaderManagerTest.kt
@@ -20,7 +20,7 @@
 import androidx.loader.app.test.LoaderOwner
 import androidx.loader.content.Loader
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
+import androidx.test.filters.LargeTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -31,7 +31,7 @@
 import java.util.concurrent.TimeUnit
 
 @RunWith(AndroidJUnit4::class)
-@MediumTest
+@LargeTest
 class LoaderManagerTest {
 
     private lateinit var loaderManager: LoaderManager
diff --git a/loader/loader/src/androidTest/java/androidx/loader/content/AsyncTaskLoaderTest.java b/loader/loader/src/androidTest/java/androidx/loader/content/AsyncTaskLoaderTest.java
index adc78b4..0659891 100644
--- a/loader/loader/src/androidTest/java/androidx/loader/content/AsyncTaskLoaderTest.java
+++ b/loader/loader/src/androidTest/java/androidx/loader/content/AsyncTaskLoaderTest.java
@@ -22,7 +22,8 @@
 import androidx.annotation.NonNull;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,13 +34,18 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 @RunWith(AndroidJUnit4.class)
-@SmallTest
+@MediumTest
 public class AsyncTaskLoaderTest {
 
     @Test
     public void testForceLoad_runsAsyncTask() throws InterruptedException {
-        TestAsyncTaskLoader loader = new TestAsyncTaskLoader(1);
-        loader.forceLoad();
+        final TestAsyncTaskLoader loader = new TestAsyncTaskLoader(1);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                loader.forceLoad();
+            }
+        });
 
         assertTrue(loader.mLoadInBackgoundLatch.await(1, TimeUnit.SECONDS));
     }
@@ -47,15 +53,20 @@
     @Test
     public void testForceLoad_runsOnCustomExecutor() throws InterruptedException {
         final CountDownLatch executorLatch = new CountDownLatch(1);
-        TestAsyncTaskLoader loader = new TestAsyncTaskLoader(1);
+        final TestAsyncTaskLoader loader = new TestAsyncTaskLoader(1);
         loader.mExecutor = new Executor() {
             @Override
-            public void execute(Runnable command) {
+            public void execute(@NonNull Runnable command) {
                 executorLatch.countDown();
                 command.run();
             }
         };
-        loader.forceLoad();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                loader.forceLoad();
+            }
+        });
 
         assertTrue(executorLatch.await(1, TimeUnit.SECONDS));
     }
@@ -70,7 +81,7 @@
     @Test
     public void testGetExecutor_forceLoadMultipleTimes_getExecutorCalledOnce()
             throws InterruptedException {
-        TestAsyncTaskLoader loader = new TestAsyncTaskLoader(3);
+        final TestAsyncTaskLoader loader = new TestAsyncTaskLoader(3);
         final AtomicInteger loadCount = new AtomicInteger(3);
         loader.registerListener(0, new Loader.OnLoadCompleteListener<Void>() {
             @Override
@@ -80,7 +91,12 @@
                 }
             }
         });
-        loader.forceLoad();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                loader.forceLoad();
+            }
+        });
 
         assertTrue(loader.mLoadInBackgoundLatch.await(1, TimeUnit.SECONDS));
         assertEquals(1, loader.mGetExecutorCallCount);
diff --git a/media/OWNERS b/media/OWNERS
index 29116e8..763e0cf 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,11 +1,6 @@
-akersten@google.com
-dwkang@google.com
 gyumin@google.com
 hdmoon@google.com
 insun@google.com
 jaewan@google.com
 jinpark@google.com
-juliacr@google.com
-robertshih@google.com
 sungsoo@google.com
-wjia@google.com
diff --git a/media/build.gradle b/media/build.gradle
index f0a49ef..863a93c 100644
--- a/media/build.gradle
+++ b/media/build.gradle
@@ -9,7 +9,7 @@
 }
 
 dependencies {
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.0.0")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
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 8f43365..a6bf8dd 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
@@ -53,8 +53,8 @@
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.core.app.BundleCompat;
-import androidx.core.app.ComponentActivity;
 import androidx.media.AudioAttributesCompat;
+import androidx.media.R;
 import androidx.media.VolumeProviderCompat;
 import androidx.versionedparcelable.ParcelUtils;
 import androidx.versionedparcelable.VersionedParcelable;
@@ -144,26 +144,11 @@
     public static final String COMMAND_ARGUMENT_INDEX =
             "android.support.v4.media.session.command.ARGUMENT_INDEX";
 
-    private static class MediaControllerExtraData extends ComponentActivity.ExtraData {
-        private final MediaControllerCompat mMediaController;
-
-        MediaControllerExtraData(MediaControllerCompat mediaController) {
-            mMediaController = mediaController;
-        }
-
-        MediaControllerCompat getMediaController() {
-            return mMediaController;
-        }
-    }
-
     /**
      * Sets a {@link MediaControllerCompat} in the {@code activity} for later retrieval via
      * {@link #getMediaController(Activity)}.
      *
-     * <p>This is compatible with {@link Activity#setMediaController(MediaController)}.
-     * If {@code activity} inherits {@link androidx.fragment.app.FragmentActivity}, the
-     * {@code mediaController} will be saved in the {@code activity}. In addition to that,
-     * on API 21 and later, {@link Activity#setMediaController(MediaController)} will be
+     * <p>On API 21 and later, {@link Activity#setMediaController(MediaController)} will also be
      * called.</p>
      *
      * @param activity The activity to set the {@code mediaController} in, must not be null.
@@ -174,10 +159,8 @@
      */
     public static void setMediaController(@NonNull Activity activity,
             MediaControllerCompat mediaController) {
-        if (activity instanceof ComponentActivity) {
-            ((ComponentActivity) activity).putExtraData(
-                    new MediaControllerExtraData(mediaController));
-        }
+        activity.getWindow().getDecorView().setTag(
+                R.id.media_controller_compat_view_tag, mediaController);
         if (android.os.Build.VERSION.SDK_INT >= 21) {
             MediaController controllerFwk = null;
             if (mediaController != null) {
@@ -200,10 +183,10 @@
      * @see #setMediaController(Activity, MediaControllerCompat)
      */
     public static MediaControllerCompat getMediaController(@NonNull Activity activity) {
-        if (activity instanceof ComponentActivity) {
-            MediaControllerExtraData extraData =
-                    ((ComponentActivity) activity).getExtraData(MediaControllerExtraData.class);
-            return extraData != null ? extraData.getMediaController() : null;
+        Object tag = activity.getWindow().getDecorView()
+                .getTag(R.id.media_controller_compat_view_tag);
+        if (tag instanceof MediaControllerCompat) {
+            return (MediaControllerCompat) tag;
         } else if (android.os.Build.VERSION.SDK_INT >= 21) {
             MediaController controllerFwk = activity.getMediaController();
             if (controllerFwk == null) {
diff --git a/benchmark/src/androidTest/AndroidManifest.xml b/media/src/main/res/values/ids.xml
similarity index 65%
copy from benchmark/src/androidTest/AndroidManifest.xml
copy to media/src/main/res/values/ids.xml
index f5ec776..2fa8e5c 100644
--- a/benchmark/src/androidTest/AndroidManifest.xml
+++ b/media/src/main/res/values/ids.xml
@@ -14,13 +14,7 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<manifest
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="androidx.benchmark.test">
 
-    <application
-        android:name="androidx.benchmark.ArgumentInjectingApplication">
-        <activity android:name="android.app.Activity"/>
-    </application>
-</manifest>
\ No newline at end of file
+<resources>
+        <item type="id" name="media_controller_compat_view_tag" />
+</resources>
\ No newline at end of file
diff --git a/media2/OWNERS b/media2/OWNERS
index 79fe538..e4d9120 100644
--- a/media2/OWNERS
+++ b/media2/OWNERS
@@ -1,13 +1,8 @@
-akersten@google.com
 andrewlewis@google.com
-dwkang@google.com
 gyumin@google.com
 hdmoon@google.com
 insun@google.com
 jaewan@google.com
 jinpark@google.com
-juliacr@google.com
-robertshih@google.com
 sungsoo@google.com
-wjia@google.com
 
diff --git a/media2/common/src/main/java/androidx/media2/common/SessionPlayer.java b/media2/common/src/main/java/androidx/media2/common/SessionPlayer.java
index e99df48..c67153a 100644
--- a/media2/common/src/main/java/androidx/media2/common/SessionPlayer.java
+++ b/media2/common/src/main/java/androidx/media2/common/SessionPlayer.java
@@ -452,8 +452,8 @@
      * @return the size of the video. The width and height of size could be 0 if there is no video
      * or the size has not been determined yet.
      * The {@link PlayerCallback} can be registered via {@link #registerPlayerCallback} to
-     * receive a notification {@link PlayerCallback#onVideoSizeChangedInternal} when the size
-     * is available.
+     * receive a notification {@link PlayerCallback#onVideoSizeChanged(SessionPlayer, VideoSize)}
+     * when the size is available.
      *
      * @hide
      */
@@ -681,7 +681,7 @@
     public abstract ListenableFuture<PlayerResult> skipToNextPlaylistItem();
 
     /**
-     * Skips to the the media item.
+     * Skips to the item in the playlist at the index.
      * <p>
      * The implementation must notify registered callbacks with
      * {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} when it's
@@ -1338,6 +1338,16 @@
         }
 
         /**
+         * @deprecated Use {@link #onVideoSizeChanged(SessionPlayer, VideoSize)} instead.
+         * @hide
+         */
+        @Deprecated
+        @RestrictTo(LIBRARY_GROUP)
+        public void onVideoSizeChangedInternal(
+                @NonNull SessionPlayer player, @NonNull MediaItem item, @NonNull VideoSize size) {
+        }
+
+        /**
          * Called to indicate the video size
          * <p>
          * The video size (width and height) could be 0 if there was no video,
@@ -1348,16 +1358,14 @@
          * is called.
          *
          * @param player the player associated with this callback
-         * @param item the MediaItem of this media item
          * @param size the size of the video
          * @see #getVideoSizeInternal()
          *
          * @hide
          */
-        // TODO: Add onVideoSizeChanged and deprecate this method (b/132928418)
+        // TODO: Unhide this method (b/132928418)
         @RestrictTo(LIBRARY_GROUP)
-        public void onVideoSizeChangedInternal(
-                @NonNull SessionPlayer player, @NonNull MediaItem item, @NonNull VideoSize size) {
+        public void onVideoSizeChanged(@NonNull SessionPlayer player, @NonNull VideoSize size) {
         }
 
         /**
@@ -1375,15 +1383,17 @@
         }
 
         /**
-         * Called when the tracks are first retrieved after media is prepared or when new tracks are
-         * found during playback.
+         * Called when the tracks of the current media item is changed such as
+         * 1) when tracks of a media item become available,
+         * 2) when new tracks are found during playback, or
+         * 3) when the current media item is changed.
          * <p>
          * When it's called, you should invalidate previous track information and use the new
          * tracks to call {@link #selectTrackInternal(SessionPlayer.TrackInfo)} or
          * {@link #deselectTrackInternal(SessionPlayer.TrackInfo)}.
          *
          * @param player the player associated with this callback
-         * @param trackInfos the list of track
+         * @param trackInfos the list of tracks. It can be empty.
          * @see #getTrackInfoInternal()
          * @hide
          */
diff --git a/media2/integration-tests/testapp/build.gradle b/media2/integration-tests/testapp/build.gradle
index b269e6a..ffefd37 100644
--- a/media2/integration-tests/testapp/build.gradle
+++ b/media2/integration-tests/testapp/build.gradle
@@ -30,7 +30,7 @@
     implementation(project(":media2:media2-player"))
     implementation(project(":media2:media2-widget"))
     implementation("androidx.appcompat:appcompat:1.0.2")
-    implementation("androidx.core:core:1.1.0-rc01")
+    implementation("androidx.core:core:1.1.0")
 }
 
 android {
diff --git a/media2/integration-tests/testapp/src/main/AndroidManifest.xml b/media2/integration-tests/testapp/src/main/AndroidManifest.xml
index 55024fe..bb0694d 100644
--- a/media2/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/media2/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -44,7 +44,7 @@
             </intent-filter>
         </activity>
 
-        <service android:name=".VideoSessionService">
+        <service android:name=".VideoSessionService" android:exported="false">
             <intent-filter>
                 <action android:name="androidx.media2.session.MediaSessionService" />
             </intent-filter>
diff --git a/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoPlayerActivity.java b/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoPlayerActivity.java
index 4e0d37e..20553f2 100644
--- a/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoPlayerActivity.java
+++ b/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoPlayerActivity.java
@@ -47,6 +47,7 @@
 import androidx.media2.widget.MediaControlView;
 import androidx.media2.widget.VideoView;
 
+import java.util.Locale;
 import java.util.concurrent.Executor;
 
 /**
@@ -172,8 +173,9 @@
                 mSpeed += 0.1f;
             }
             mMediaController.setPlaybackSpeed(mSpeed);
-            Toast.makeText(this, "speed rate: " + String.format("%.2f", mSpeed), Toast.LENGTH_SHORT)
-                    .show();
+            Toast.makeText(this,
+                    "speed rate: " + String.format(Locale.US, "%.2f", mSpeed),
+                    Toast.LENGTH_SHORT).show();
         }
         return super.onTouchEvent(ev);
     }
diff --git a/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSelectorActivity.java b/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSelectorActivity.java
index 123b7a0..36a69f1 100644
--- a/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSelectorActivity.java
+++ b/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSelectorActivity.java
@@ -290,12 +290,12 @@
             }
         }
 
-        // now add the the sorted directories to the result set.
+        // now add the sorted directories to the result set.
         for (VideoItem vi : dirs.values()) {
             retVal.add(vi);
         }
 
-        // finally add the the sorted files to the result set.
+        // finally add the sorted files to the result set.
         for (VideoItem vi : files.values()) {
             retVal.add(vi);
         }
diff --git a/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSessionService.java b/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSessionService.java
index ca7fbf4..9d513d3 100644
--- a/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSessionService.java
+++ b/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSessionService.java
@@ -16,6 +16,8 @@
 
 package androidx.media2.integration.testapp;
 
+import static androidx.media2.common.MediaMetadata.METADATA_KEY_MEDIA_ID;
+
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -34,6 +36,7 @@
 import androidx.media2.session.MediaSession;
 import androidx.media2.session.MediaSessionService;
 
+import java.lang.ref.WeakReference;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -45,7 +48,6 @@
 
     private MediaPlayer mMediaPlayer;
     private MediaSession mMediaSession;
-    private UriMediaItem mCurrentItem;
     private AudioAttributesCompat mAudioAttributes;
 
     @Override
@@ -106,29 +108,29 @@
         public MediaItem onCreateMediaItem(@NonNull MediaSession session,
                 @NonNull MediaSession.ControllerInfo controller, @NonNull String mediaId) {
             MediaMetadata metadata = new MediaMetadata.Builder()
-                    .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, mediaId)
+                    .putString(METADATA_KEY_MEDIA_ID, mediaId)
                     .build();
-            mCurrentItem = new UriMediaItem.Builder(Uri.parse(mediaId))
+            UriMediaItem currentItem = new UriMediaItem.Builder(Uri.parse(mediaId))
                     .setMetadata(metadata)
                     .build();
-            MetadataExtractTask task = new MetadataExtractTask(mCurrentItem,
+            MetadataExtractTask task = new MetadataExtractTask(currentItem,
                     VideoSessionService.this);
             task.execute();
             // TODO: Temporary fix for multiple calls of setMediaItem not working properly.
             //  (b/135728285)
             mMediaPlayer.reset();
             mMediaPlayer.setAudioAttributes(mAudioAttributes);
-            return mCurrentItem;
+            return currentItem;
         }
     }
 
-    private class MetadataExtractTask extends AsyncTask<Void, Void, MediaMetadata> {
+    private static class MetadataExtractTask extends AsyncTask<Void, Void, MediaMetadata> {
         private MediaItem mItem;
-        private Context mContext;
+        private WeakReference<Context> mRefContext;
 
         MetadataExtractTask(MediaItem mediaItem, Context context) {
             mItem = mediaItem;
-            mContext = context;
+            mRefContext = new WeakReference<>(context);
         }
 
         @Override
@@ -145,25 +147,23 @@
 
         MediaMetadata extractMetadata(MediaItem mediaItem) {
             MediaMetadataRetriever retriever = null;
+            Context context = mRefContext.get();
             try {
                 if (mediaItem == null) {
                     return null;
-                } else if (mediaItem instanceof UriMediaItem) {
+                } else if (mediaItem instanceof UriMediaItem && context != null) {
                     Uri uri = ((UriMediaItem) mediaItem).getUri();
                     retriever = new MediaMetadataRetriever();
-                    retriever.setDataSource(mContext, uri);
+                    retriever.setDataSource(mRefContext.get(), uri);
                 }
             } catch (IllegalArgumentException e) {
-                retriever = null;
+                return mediaItem.getMetadata();
             }
 
-            // Do not extract metadata of a media item which is not the current item.
-            if (mediaItem != mCurrentItem) {
-                if (retriever != null) {
-                    retriever.release();
-                }
-                return null;
+            if (retriever == null) {
+                return mediaItem.getMetadata();
             }
+
             String title = extractString(retriever, MediaMetadataRetriever.METADATA_KEY_TITLE);
             String musicArtistText = extractString(retriever,
                     MediaMetadataRetriever.METADATA_KEY_ARTIST);
@@ -173,14 +173,9 @@
                 retriever.release();
             }
 
-            // Set duration and title values as MediaMetadata for MediaControlView
-            MediaMetadata.Builder builder = new MediaMetadata.Builder(mCurrentItem.getMetadata());
-
+            MediaMetadata.Builder builder = new MediaMetadata.Builder(mediaItem.getMetadata());
             builder.putString(MediaMetadata.METADATA_KEY_TITLE, title);
             builder.putString(MediaMetadata.METADATA_KEY_ARTIST, musicArtistText);
-            builder.putString(
-                    MediaMetadata.METADATA_KEY_MEDIA_ID, mediaItem.getMediaId());
-            builder.putLong(MediaMetadata.METADATA_KEY_PLAYABLE, 1);
             builder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, musicAlbumBitmap);
             return builder.build();
         }
diff --git a/media2/integration-tests/testapp/src/main/res/layout/activity_video_selector.xml b/media2/integration-tests/testapp/src/main/res/layout/activity_video_selector.xml
index 510c3a1..99323ec 100644
--- a/media2/integration-tests/testapp/src/main/res/layout/activity_video_selector.xml
+++ b/media2/integration-tests/testapp/src/main/res/layout/activity_video_selector.xml
@@ -57,8 +57,7 @@
         android:layout_height="wrap_content"
         android:layout_alignParentBottom="true"
         android:layout_alignParentLeft="true"
-        android:layout_toLeftOf="@id/play_button"
-        android:editable="true" />
+        android:layout_toLeftOf="@id/play_button" />
 
     <ListView
         android:id="@+id/select_list"
diff --git a/media2/media2-exoplayer/src/main/libs/exoplayer-media2.jar b/media2/media2-exoplayer/src/main/libs/exoplayer-media2.jar
index dffa337..121b6d1 100755
--- a/media2/media2-exoplayer/src/main/libs/exoplayer-media2.jar
+++ b/media2/media2-exoplayer/src/main/libs/exoplayer-media2.jar
Binary files differ
diff --git a/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayer2Test.java b/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayer2Test.java
index 2f84cbd..b8fefc9 100644
--- a/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayer2Test.java
+++ b/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayer2Test.java
@@ -1730,8 +1730,6 @@
             public void onInfo(MediaPlayer2 mp, MediaItem item, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
                     mOnPrepareCalled.signal();
-                } else if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
-                    mOnInfoCalled.signal();
                 }
             }
 
@@ -1755,6 +1753,17 @@
                 assertNotNull(data.getData());
                 mOnSubtitleDataCalled.signal();
             }
+
+            @Override
+            public void onTrackInfoChanged(@NonNull MediaPlayer2 mp,
+                    @NonNull List<TrackInfo> tracks) {
+                assertNotNull(tracks);
+                if (tracks.size() < 3) {
+                    // This callback can be called before tracks are available after setMediaItem.
+                    return;
+                }
+                mTracksFullyFound.signal();
+            }
         };
         synchronized (mEventCbLock) {
             mEventCallbacks.add(ecb);
@@ -1773,10 +1782,7 @@
 
         // Closed caption tracks are in-band.
         // So, those tracks will be found after processing a number of frames.
-        mOnInfoCalled.waitForSignal(1500);
-
-        mOnInfoCalled.reset();
-        mOnInfoCalled.waitForSignal(1500);
+        assertTrue(mTracksFullyFound.waitForSignal(3000));
 
         readTracks();
 
@@ -1816,8 +1822,6 @@
             public void onInfo(MediaPlayer2 mp, MediaItem item, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
                     mOnPrepareCalled.signal();
-                } else if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
-                    mOnInfoCalled.signal();
                 }
             }
 
@@ -1836,6 +1840,17 @@
                 assertNotNull(data.getData());
                 mOnSubtitleDataCalled.signal();
             }
+
+            @Override
+            public void onTrackInfoChanged(@NonNull MediaPlayer2 mp,
+                    @NonNull List<TrackInfo> tracks) {
+                assertNotNull(tracks);
+                if (tracks.size() < 3) {
+                    // This callback can be called before tracks are available after setMediaItem.
+                    return;
+                }
+                mTracksFullyFound.signal();
+            }
         };
         synchronized (mEventCbLock) {
             mEventCallbacks.add(ecb);
@@ -1854,10 +1869,7 @@
 
         // Closed caption tracks are in-band.
         // So, those tracks will be found after processing a number of frames.
-        mOnInfoCalled.waitForSignal(1500);
-
-        mOnInfoCalled.reset();
-        mOnInfoCalled.waitForSignal(1500);
+        assertTrue(mTracksFullyFound.waitForSignal(3000));
 
         readTracks();
 
@@ -1893,8 +1905,6 @@
             public void onInfo(MediaPlayer2 mp, MediaItem item, int what, int extra) {
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
                     mOnPrepareCalled.signal();
-                } else if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
-                    mOnInfoCalled.signal();
                 }
             }
 
@@ -1905,6 +1915,17 @@
                     mOnPlayCalled.signal();
                 }
             }
+
+            @Override
+            public void onTrackInfoChanged(@NonNull MediaPlayer2 mp,
+                    @NonNull List<TrackInfo> tracks) {
+                assertNotNull(tracks);
+                if (tracks.size() < 3) {
+                    // This callback can be called before tracks are available after setMediaItem.
+                    return;
+                }
+                mTracksFullyFound.signal();
+            }
         };
         synchronized (mEventCbLock) {
             mEventCallbacks.add(ecb);
@@ -1924,10 +1945,7 @@
         // The media metadata will be changed while playing since closed caption tracks are in-band
         // and those tracks will be found after processing a number of frames. These tracks will be
         // found within one second.
-        mOnInfoCalled.waitForSignal(1500);
-
-        mOnInfoCalled.reset();
-        mOnInfoCalled.waitForSignal(1500);
+        assertTrue(mTracksFullyFound.waitForSignal(3000));
 
         readTracks();
         assertEquals(2, mSubtitleTrackInfos.size());
@@ -2189,8 +2207,6 @@
 
             @Override
             public void onInfo(MediaPlayer2 mp, MediaItem item, int what, int extra) {
-                mOnInfoCalled.signal();
-
                 if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
                     mOnPrepareCalled.signal();
                 } else if (what == MediaPlayer2.MEDIA_INFO_DATA_SOURCE_END) {
diff --git a/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayer2TestBase.java b/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayer2TestBase.java
index 4b6da5f..524baa3 100644
--- a/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayer2TestBase.java
+++ b/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayer2TestBase.java
@@ -80,7 +80,7 @@
     protected Monitor mOnDeselectTrackCalled = new Monitor();
     protected Monitor mOnSeekCompleteCalled = new Monitor();
     protected Monitor mOnCompletionCalled = new Monitor();
-    protected Monitor mOnInfoCalled = new Monitor();
+    protected Monitor mTracksFullyFound = new Monitor();
     protected Monitor mOnErrorCalled = new Monitor();
     protected Monitor mOnMediaTimeDiscontinuityCalled = new Monitor();
     protected int mCallStatus;
@@ -352,6 +352,7 @@
                     }
                 }
             }
+
             @Override
             public  void onSubtitleData(@NonNull MediaPlayer2 mp, @NonNull MediaItem item,
                     @NonNull TrackInfo track, @NonNull SubtitleData data) {
@@ -361,6 +362,16 @@
                     }
                 }
             }
+
+            @Override
+            public void onTrackInfoChanged(@NonNull MediaPlayer2 mp,
+                    @NonNull List<TrackInfo> tracks) {
+                synchronized (cbLock) {
+                    for (MediaPlayer2.EventCallback ecb : ecbs) {
+                        ecb.onTrackInfoChanged(mp, tracks);
+                    }
+                }
+            }
         });
     }
 
diff --git a/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java b/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java
index 8214453..00e85db 100644
--- a/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java
+++ b/media2/player/src/androidTest/java/androidx/media2/player/MediaPlayerTest.java
@@ -90,8 +90,7 @@
     private final List<TrackInfo> mSubtitleTrackInfos = new ArrayList<>();
     private TrackInfo mSelectedTrack = null;
     private final Monitor mOnSubtitleDataCalled = new Monitor();
-    private final Monitor mOnInfoCalled = new Monitor();
-    private final Monitor mOnTrackInfoChangedCalled = new Monitor();
+    private final Monitor mTracksFullyFound = new Monitor();
     private final Monitor mOnMediaTimeDiscontinuityCalled = new Monitor();
     private final Monitor mOnErrorCalled = new Monitor();
 
@@ -200,6 +199,16 @@
         MediaPlayer.PlayerCallback callback = new MediaPlayer.PlayerCallback() {
             @Override
             public void onVideoSizeChanged(MediaPlayer mp, MediaItem dsd, VideoSize size) {
+                assertVideoSizeEquals(size);
+            }
+
+            @Override
+            public void onVideoSizeChanged(@NonNull SessionPlayer player,
+                    @NonNull androidx.media2.common.VideoSize size) {
+                assertVideoSizeEquals(new VideoSize(size.getWidth(), size.getHeight()));
+            }
+
+            private void assertVideoSizeEquals(VideoSize size) {
                 if (size.getWidth() == 0 && size.getHeight() == 0) {
                     // A size of 0x0 can be sent initially one time when using NuPlayer.
                     assertFalse(onVideoSizeChangedCalled.isSignalled());
@@ -227,7 +236,7 @@
         mPlayer.prepare();
         mPlayer.play();
 
-        onVideoSizeChangedCalled.waitForSignal();
+        onVideoSizeChangedCalled.waitForCountedSignals(2);
         onVideoRenderingStartCalled.waitForSignal();
 
         mPlayer.setPlayerVolume(volume);
@@ -300,6 +309,16 @@
         MediaPlayer.PlayerCallback callback = new MediaPlayer.PlayerCallback() {
             @Override
             public void onVideoSizeChanged(MediaPlayer mp, MediaItem dsd, VideoSize size) {
+                assertVideoSizeEquals(size);
+            }
+
+            @Override
+            public void onVideoSizeChanged(@NonNull SessionPlayer player,
+                    @NonNull androidx.media2.common.VideoSize size) {
+                assertVideoSizeEquals(new VideoSize(size.getWidth(), size.getHeight()));
+            }
+
+            private void assertVideoSizeEquals(VideoSize size) {
                 if (size.getWidth() == 0 && size.getHeight() == 0) {
                     // A size of 0x0 can be sent initially one time when using NuPlayer.
                     assertFalse(onVideoSizeChangedCalled.isSignalled());
@@ -327,7 +346,7 @@
         mPlayer.prepare();
         mPlayer.play();
 
-        onVideoSizeChangedCalled.waitForSignal();
+        onVideoSizeChangedCalled.waitForCountedSignals(2);
         onVideoRenderingStartCalled.waitForSignal();
     }
 
@@ -616,14 +635,6 @@
         mInstrumentation.waitForIdleSync();
 
         MediaPlayer.PlayerCallback callback = new MediaPlayer.PlayerCallback() {
-            // TODO: Change this to onTrackInfoChanged callback
-            @Override
-            public void onInfo(MediaPlayer mp, MediaItem dsd, int what, int extra) {
-                if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
-                    mOnInfoCalled.signal();
-                }
-            }
-
             @Override
             public void onSubtitleData(@NonNull SessionPlayer player, @NonNull MediaItem item,
                     @NonNull SessionPlayer.TrackInfo track, @NonNull SubtitleData data) {
@@ -631,19 +642,27 @@
                     mOnSubtitleDataCalled.signal();
                 }
             }
+
+            @Override
+            public void onTrackInfoChanged(@NonNull SessionPlayer player,
+                    @NonNull List<SessionPlayer.TrackInfo> tracks) {
+                assertNotNull(tracks);
+                if (tracks.size() < 3) {
+                    // This callback can be called before tracks are available after setMediaItem.
+                    return;
+                }
+                mTracksFullyFound.signal();
+            }
         };
         mPlayer.registerPlayerCallback(mExecutor, callback);
         mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
         mPlayer.prepare();
         mPlayer.play().get();
-        assertTrue(mPlayer.getPlayerState() == MediaPlayer.PLAYER_STATE_PLAYING);
+        assertEquals(MediaPlayer.PLAYER_STATE_PLAYING, mPlayer.getPlayerState());
 
         // Closed caption tracks are in-band.
         // So, those tracks will be found after processing a number of frames.
-        mOnInfoCalled.waitForSignal(1500);
-
-        mOnInfoCalled.reset();
-        mOnInfoCalled.waitForSignal(1500);
+        assertTrue(mTracksFullyFound.waitForSignal(3000));
 
         readTracks();
 
@@ -673,14 +692,6 @@
         }
 
         MediaPlayer.PlayerCallback callback = new MediaPlayer.PlayerCallback() {
-            // TODO: Change this to onTrackInfoChanged callback
-            @Override
-            public void onInfo(MediaPlayer mp, MediaItem dsd, int what, int extra) {
-                if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
-                    mOnInfoCalled.signal();
-                }
-            }
-
             @Override
             public void onSubtitleData(@NonNull SessionPlayer player, @NonNull MediaItem item,
                     @NonNull SessionPlayer.TrackInfo track, @NonNull SubtitleData data) {
@@ -688,19 +699,27 @@
                     mOnSubtitleDataCalled.signal();
                 }
             }
+
+            @Override
+            public void onTrackInfoChanged(@NonNull SessionPlayer player,
+                    @NonNull List<SessionPlayer.TrackInfo> tracks) {
+                assertNotNull(tracks);
+                if (tracks.size() < 3) {
+                    // This callback can be called before tracks are available after setMediaItem.
+                    return;
+                }
+                mTracksFullyFound.signal();
+            }
         };
         mPlayer.registerPlayerCallback(mExecutor, callback);
         mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
         mPlayer.prepare();
         mPlayer.play().get();
-        assertTrue(mPlayer.getPlayerState() == MediaPlayer.PLAYER_STATE_PLAYING);
+        assertEquals(MediaPlayer.PLAYER_STATE_PLAYING, mPlayer.getPlayerState());
 
         // Closed caption tracks are in-band.
         // So, those tracks will be found after processing a number of frames.
-        mOnInfoCalled.waitForSignal(1500);
-
-        mOnInfoCalled.reset();
-        mOnInfoCalled.waitForSignal(1500);
+        assertTrue(mTracksFullyFound.waitForSignal(3000));
 
         readTracks();
         assertEquals(mSelectedTrack, mPlayer.getSelectedTrack(TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE));
@@ -727,27 +746,27 @@
         }
 
         MediaPlayer.PlayerCallback callback = new MediaPlayer.PlayerCallback() {
-            // TODO: Change this to onTrackInfoChanged callback
             @Override
-            public void onInfo(MediaPlayer mp, MediaItem dsd, int what, int extra) {
-                if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
-                    mOnInfoCalled.signal();
+            public void onTrackInfoChanged(@NonNull SessionPlayer player,
+                    @NonNull List<SessionPlayer.TrackInfo> tracks) {
+                assertNotNull(tracks);
+                if (tracks.size() < 3) {
+                    // This callback can be called before tracks are available after setMediaItem.
+                    return;
                 }
+                mTracksFullyFound.signal();
             }
         };
         mPlayer.registerPlayerCallback(mExecutor, callback);
         mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
         mPlayer.prepare();
         mPlayer.play().get();
-        assertTrue(mPlayer.getPlayerState() == MediaPlayer.PLAYER_STATE_PLAYING);
+        assertEquals(MediaPlayer.PLAYER_STATE_PLAYING, mPlayer.getPlayerState());
 
         // The media metadata will be changed while playing since closed caption tracks are in-band
         // and those tracks will be found after processing a number of frames. These tracks will be
         // found within one second.
-        mOnInfoCalled.waitForSignal(1500);
-
-        mOnInfoCalled.reset();
-        mOnInfoCalled.waitForSignal(1500);
+        assertTrue(mTracksFullyFound.waitForSignal(3000));
 
         readTracks();
         assertEquals(2, mSubtitleTrackInfos.size());
@@ -766,14 +785,19 @@
         MediaPlayer.PlayerCallback callback = new MediaPlayer.PlayerCallback() {
             @Override
             public void onTrackInfoChanged(@NonNull SessionPlayer player,
-                    @NonNull List<SessionPlayer.TrackInfo> trackInfos) {
-                mOnTrackInfoChangedCalled.signal();
+                    @NonNull List<SessionPlayer.TrackInfo> tracks) {
+                assertNotNull(tracks);
+                if (tracks.size() < 2) {
+                    // This callback can be called before tracks are available after setMediaItem.
+                    return;
+                }
+                mTracksFullyFound.signal();
             }
         };
         mPlayer.registerPlayerCallback(mExecutor, callback);
         mPlayer.prepare();
 
-        mOnTrackInfoChangedCalled.waitForSignal(1500);
+        mTracksFullyFound.waitForSignal(1500);
 
         readTracks();
 
diff --git a/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java b/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
index fdad296..7e62724 100644
--- a/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
+++ b/media2/player/src/main/java/androidx/media2/player/MediaPlayer.java
@@ -2308,16 +2308,15 @@
      * </p>
      * @param trackInfo metadata corresponding to the track to be selected. A {@code trackInfo}
      * object can be obtained from {@link #getTrackInfo()}.
-     * Note that the {@link TrackInfo}s may become invalid
-     * when {@link PlayerCallback#onInfo(MediaPlayer, MediaItem, int, int)} is called with
-     * {@link #MEDIA_INFO_METADATA_UPDATE}.
      *
      * @see #getTrackInfo
      * @return a {@link ListenableFuture} which represents the pending completion of the command.
      * {@link SessionPlayer.PlayerResult} will be delivered when the command completed.
      */
     // TODO: support subtitle track selection  (b/130312596)
-    // TODO: revise doc when onTrackInfoChanged is becoming public (b/132928418)
+    // TODO: Revise doc to let developers know the tracks from getTrackInfo may be invalidated
+    //       when onTrackInfoChanged is called after onTrackInfoChanged is becoming public
+    //       (b/132928418).
     @NonNull
     public ListenableFuture<PlayerResult> selectTrack(@NonNull final TrackInfo trackInfo) {
         return selectTrackInternal(trackInfo.toInternal());
@@ -2331,9 +2330,6 @@
      * </p>
      * @param trackInfo metadata corresponding to the track to be selected. A {@code trackInfo}
      * object can be obtained from {@link #getTrackInfo()}.
-     * Note that the {@link TrackInfo}s may become invalid
-     * when {@link PlayerCallback#onInfo(MediaPlayer, MediaItem, int, int)} is called with
-     * {@link #MEDIA_INFO_METADATA_UPDATE}.
      *
      * @see #getTrackInfo
      * @return a {@link ListenableFuture} which represents the pending completion of the command.
@@ -2341,7 +2337,9 @@
      *
      * @hide  TODO: unhide this when we support subtitle track selection (b/130312596)
      */
-    // TODO: revise doc when onTrackInfoChanged is becoming public (b/132928418)
+    // TODO: Revise doc to let developers know the tracks from getTrackInfo may be invalidated
+    //       when onTrackInfoChanged is called after onTrackInfoChanged is becoming public
+    //       (b/132928418).
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @NonNull
     public ListenableFuture<PlayerResult> deselectTrack(@NonNull final TrackInfo trackInfo) {
@@ -3014,13 +3012,16 @@
                     break;
                 case MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE:
                 case MediaPlayer2.CALL_COMPLETED_SKIP_TO_NEXT:
+                    final List<SessionPlayer.TrackInfo> tracks = mp.getTrackInfo();
+                    final androidx.media2.common.VideoSize videoSize = getVideoSizeInternal();
                     notifySessionPlayerCallback(new SessionPlayerCallbackNotifier() {
                         @Override
                         public void callCallback(
                                 SessionPlayer.PlayerCallback callback) {
                             callback.onCurrentMediaItemChanged(MediaPlayer.this, item);
-                            callback.onVideoSizeChangedInternal(MediaPlayer.this,
-                                    getCurrentMediaItem(), getVideoSizeInternal());
+                            callback.onVideoSizeChanged(MediaPlayer.this, videoSize);
+                            callback.onVideoSizeChangedInternal(MediaPlayer.this, item, videoSize);
+                            callback.onTrackInfoChanged(MediaPlayer.this, tracks);
                         }
                     });
                     break;
@@ -3128,6 +3129,7 @@
                 notifySessionPlayerCallback(new SessionPlayerCallbackNotifier() {
                     @Override
                     public void callCallback(SessionPlayer.PlayerCallback callback) {
+                        callback.onVideoSizeChanged(MediaPlayer.this, commonSize);
                         callback.onVideoSizeChangedInternal(MediaPlayer.this, item, commonSize);
                     }
                 });
@@ -3166,14 +3168,6 @@
                     setBufferingState(item, BUFFERING_STATE_BUFFERING_AND_STARVED);
                     break;
                 case MediaPlayer2.MEDIA_INFO_PREPARED:
-                    notifySessionPlayerCallback(new SessionPlayerCallbackNotifier() {
-                        @Override
-                        public void callCallback(SessionPlayer.PlayerCallback callback) {
-                            callback.onTrackInfoChanged(MediaPlayer.this, getTrackInfoInternal());
-                        }
-                    });
-                    setBufferingState(item, BUFFERING_STATE_BUFFERING_AND_PLAYABLE);
-                    break;
                 case MediaPlayer2.MEDIA_INFO_BUFFERING_END:
                     setBufferingState(item, BUFFERING_STATE_BUFFERING_AND_PLAYABLE);
                     break;
@@ -3191,14 +3185,6 @@
                         }
                     });
                     break;
-                case MediaPlayer2.MEDIA_INFO_METADATA_UPDATE:
-                    notifySessionPlayerCallback(new SessionPlayerCallbackNotifier() {
-                        @Override
-                        public void callCallback(SessionPlayer.PlayerCallback callback) {
-                            callback.onTrackInfoChanged(MediaPlayer.this, getTrackInfoInternal());
-                        }
-                    });
-                    break;
             }
             if (sInfoCodeMap.containsKey(mp2What)) {
                 final int what = sInfoCodeMap.get(mp2What);
@@ -3243,6 +3229,13 @@
                 }
             });
         }
+
+        @Override
+        public void onTrackInfoChanged(@NonNull MediaPlayer2 mp,
+                @NonNull List<SessionPlayer.TrackInfo> tracks) {
+            notifySessionPlayerCallback(callback -> callback.onTrackInfoChanged(MediaPlayer.this,
+                    tracks));
+        }
     }
 
     /**
@@ -3265,9 +3258,12 @@
                 @NonNull MediaPlayer mp, @NonNull MediaItem item, @NonNull VideoSize size) { }
 
         /**
+         * @deprecated Use
+         * {@link #onVideoSizeChanged(SessionPlayer, androidx.media2.common.VideoSize)} instead.
          * @hide
          */
         @RestrictTo(LIBRARY_GROUP)
+        @Deprecated
         @Override
         public void onVideoSizeChangedInternal(
                 @NonNull SessionPlayer player, @NonNull MediaItem item,
diff --git a/media2/player/src/main/java/androidx/media2/player/MediaPlayer2.java b/media2/player/src/main/java/androidx/media2/player/MediaPlayer2.java
index 88d7fa5..379bfac 100644
--- a/media2/player/src/main/java/androidx/media2/player/MediaPlayer2.java
+++ b/media2/player/src/main/java/androidx/media2/player/MediaPlayer2.java
@@ -777,9 +777,8 @@
      * </p>
      * @param trackId the id of the track to be selected. The id can be obtained by calling
      * {@link TrackInfo#getId()} to an {@link TrackInfo} returned by {@link #getTrackInfo()}.
-     * Note that the {@link TrackInfo}s may become invalid
-     * when {@link EventCallback#onInfo(MediaPlayer2, MediaItem, int, int)} is called with
-     * {@code what} of {@link #MEDIA_INFO_PREPARED} or {@link #MEDIA_INFO_METADATA_UPDATE}.
+     * Note that the {@link TrackInfo}s may become invalid when
+     * {@link EventCallback#onTrackInfoChanged} is called.
      *
      * @see TrackInfo#getId()
      * @see #getTrackInfo
@@ -798,9 +797,8 @@
      * </p>
      * @param trackId the id of the track to be deselected. The id can be obtained by calling
      * {@link TrackInfo#getId()} to an {@link TrackInfo} returned by {@link #getTrackInfo()} or
-     * {@link #getSelectedTrack(int)}. Note that the {@link TrackInfo}s may become invalid
-     * when {@link EventCallback#onInfo(MediaPlayer2, MediaItem, int, int)} is called with
-     * {@code what} of {@link #MEDIA_INFO_PREPARED} or {@link #MEDIA_INFO_METADATA_UPDATE}.
+     * {@link #getSelectedTrack(int)}. Note that the {@link TrackInfo}s may become invalid when
+     * {@link EventCallback#onTrackInfoChanged} is called.
      *
      * @see TrackInfo#getId()
      * @see #getTrackInfo
@@ -926,6 +924,20 @@
          */
         public void onSubtitleData(@NonNull MediaPlayer2 mp, @NonNull MediaItem item,
                 @NonNull TrackInfo track, @NonNull SubtitleData data) { }
+
+        /**
+         * Called when the tracks of the current media item is changed such as
+         * 1) when tracks of a media item become available, or
+         * 2) when new tracks are found during playback.
+         * <p>
+         * When it's called, the previous tracks may be invalidated so it's recommended to use the
+         * most recent tracks to call {@link #selectTrack} or {@link #deselectTrack}.
+         *
+         * @param mp the player associated with this callback
+         * @param tracks the list of tracks. It can be empty.
+         */
+        public void onTrackInfoChanged(@NonNull MediaPlayer2 mp,
+                @NonNull List<TrackInfo> tracks) { }
     }
 
     /**
diff --git a/media2/player/src/main/java/androidx/media2/player/exoplayer/DurationProvidingMediaSource.java b/media2/player/src/main/java/androidx/media2/player/exoplayer/DurationProvidingMediaSource.java
index 79aeb0a..5c52b8f 100644
--- a/media2/player/src/main/java/androidx/media2/player/exoplayer/DurationProvidingMediaSource.java
+++ b/media2/player/src/main/java/androidx/media2/player/exoplayer/DurationProvidingMediaSource.java
@@ -20,7 +20,6 @@
 
 import android.annotation.SuppressLint;
 
-import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.media2.exoplayer.external.C;
 import androidx.media2.exoplayer.external.Timeline;
@@ -65,6 +64,13 @@
     }
 
     @Override
+    protected void onChildSourceInfoRefreshed(Void id,
+            MediaSource mediaSource, Timeline timeline) {
+        mCurrentTimeline = timeline;
+        refreshSourceInfo(timeline);
+    }
+
+    @Override
     public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
         return mMediaSource.createPeriod(id, allocator, startPositionUs);
     }
@@ -74,11 +80,4 @@
         mMediaSource.releasePeriod(mediaPeriod);
     }
 
-    @Override
-    protected void onChildSourceInfoRefreshed(
-            Void id, MediaSource mediaSource, Timeline timeline, @Nullable Object manifest) {
-        mCurrentTimeline = timeline;
-        refreshSourceInfo(timeline, manifest);
-    }
-
 }
diff --git a/media2/player/src/main/java/androidx/media2/player/exoplayer/ExoPlayerMediaPlayer2Impl.java b/media2/player/src/main/java/androidx/media2/player/exoplayer/ExoPlayerMediaPlayer2Impl.java
index 7b8568e..74c82e5 100644
--- a/media2/player/src/main/java/androidx/media2/player/exoplayer/ExoPlayerMediaPlayer2Impl.java
+++ b/media2/player/src/main/java/androidx/media2/player/exoplayer/ExoPlayerMediaPlayer2Impl.java
@@ -667,8 +667,9 @@
     }
 
     @Override
-    public void onMetadataChanged(MediaItem mediaItem) {
-        notifyOnInfo(mediaItem, MEDIA_INFO_METADATA_UPDATE);
+    public void onTrackInfoChanged(@NonNull final List<TrackInfo> tracks) {
+        notifyMediaPlayer2Event(cb -> cb.onTrackInfoChanged(ExoPlayerMediaPlayer2Impl.this,
+                tracks));
     }
 
     @Override
diff --git a/media2/player/src/main/java/androidx/media2/player/exoplayer/ExoPlayerWrapper.java b/media2/player/src/main/java/androidx/media2/player/exoplayer/ExoPlayerWrapper.java
index c578ad8..f7b801f 100644
--- a/media2/player/src/main/java/androidx/media2/player/exoplayer/ExoPlayerWrapper.java
+++ b/media2/player/src/main/java/androidx/media2/player/exoplayer/ExoPlayerWrapper.java
@@ -103,8 +103,8 @@
         /** Called when the player is prepared. */
         void onPrepared(MediaItem mediaItem);
 
-        /** Called when metadata (e.g., the set of available tracks) changes. */
-        void onMetadataChanged(MediaItem mediaItem);
+        /** Called when the list of available tracks changes. */
+        void onTrackInfoChanged(@NonNull List<TrackInfo> tracks);
 
         /** Called when a seek request has completed. */
         void onSeekCompleted();
@@ -567,8 +567,8 @@
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     void handleTextRendererChannelAvailable(int type, int channel) {
         mTrackSelector.handleTextRendererChannelAvailable(type, channel);
-        if (mTrackSelector.hasPendingMetadataUpdate()) {
-            mListener.onMetadataChanged(getCurrentMediaItem());
+        if (mTrackSelector.hasPendingTracksUpdate()) {
+            mListener.onTrackInfoChanged(getTrackInfo());
         }
     }
 
@@ -576,8 +576,8 @@
     void handlePlayerTracksChanged(TrackSelectionArray trackSelections) {
         MediaItem currentMediaItem = getCurrentMediaItem();
         mTrackSelector.handlePlayerTracksChanged(currentMediaItem, trackSelections);
-        if (mTrackSelector.hasPendingMetadataUpdate()) {
-            mListener.onMetadataChanged(currentMediaItem);
+        if (mTrackSelector.hasPendingTracksUpdate()) {
+            mListener.onTrackInfoChanged(getTrackInfo());
         }
     }
 
diff --git a/media2/player/src/main/java/androidx/media2/player/exoplayer/TrackSelector.java b/media2/player/src/main/java/androidx/media2/player/exoplayer/TrackSelector.java
index fcf14e3..6f67804 100644
--- a/media2/player/src/main/java/androidx/media2/player/exoplayer/TrackSelector.java
+++ b/media2/player/src/main/java/androidx/media2/player/exoplayer/TrackSelector.java
@@ -74,7 +74,7 @@
     private final SparseArray<InternalTrackInfo> mMetadataTracks;
     private final SparseArray<InternalTextTrackInfo> mTextTracks;
 
-    private boolean mPendingMetadataUpdate;
+    private boolean mPendingTracksUpdate;
     private InternalTrackInfo mSelectedAudioTrack;
     private InternalTrackInfo mSelectedVideoTrack;
     private InternalTrackInfo mSelectedMetadataTrack;
@@ -109,7 +109,7 @@
         final boolean itemChanged = mCurrentMediaItem != item;
         mCurrentMediaItem = item;
 
-        mPendingMetadataUpdate = true;
+        mPendingTracksUpdate = true;
 
         // Clear all selection state.
         mDefaultTrackSelector.setParameters(
@@ -233,14 +233,14 @@
             InternalTextTrackInfo textTrack = new InternalTextTrackInfo(
                     mPlayerTextTrackIndex, type, /* format= */ null, channel, mNextTrackId++);
             mTextTracks.put(textTrack.mExternalTrackInfo.getId(), textTrack);
-            mPendingMetadataUpdate = true;
+            mPendingTracksUpdate = true;
         }
     }
 
-    public boolean hasPendingMetadataUpdate() {
-        boolean pendingMetadataUpdate = mPendingMetadataUpdate;
-        mPendingMetadataUpdate = false;
-        return pendingMetadataUpdate;
+    public boolean hasPendingTracksUpdate() {
+        boolean pendingTracksUpdate = mPendingTracksUpdate;
+        mPendingTracksUpdate = false;
+        return pendingTracksUpdate;
     }
 
     public TrackInfo getSelectedTrack(int trackType) {
diff --git a/media2/session/api/1.1.0-alpha01.txt b/media2/session/api/1.1.0-alpha01.txt
index d16e2fb..4d96ad8 100644
--- a/media2/session/api/1.1.0-alpha01.txt
+++ b/media2/session/api/1.1.0-alpha01.txt
@@ -190,6 +190,7 @@
     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 d16e2fb..4d96ad8 100644
--- a/media2/session/api/current.txt
+++ b/media2/session/api/current.txt
@@ -190,6 +190,7 @@
     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/androidTest/java/androidx/media2/session/MediaControllerTest.java b/media2/session/src/androidTest/java/androidx/media2/session/MediaControllerTest.java
index 3373a35..57af209 100644
--- a/media2/session/src/androidTest/java/androidx/media2/session/MediaControllerTest.java
+++ b/media2/session/src/androidTest/java/androidx/media2/session/MediaControllerTest.java
@@ -308,15 +308,16 @@
     }
 
     @Test
-    public void testSetMediaItem() throws InterruptedException {
+    public void testSetMediaItem() throws Exception {
         prepareLooper();
-        final MediaItem item = TestUtils.createMediaItemWithMetadata();
-        mController.setMediaItem(item.getMetadata()
-                .getString(MediaMetadata.METADATA_KEY_MEDIA_ID));
+        String mediaId = "testSetMediaItem";
+        int resultCode = mController.setMediaItem(mediaId).get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+                .getResultCode();
+        assertEquals(RESULT_SUCCESS, resultCode);
         assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
         assertNull(mPlayer.mMetadata);
-        assertEquals(item.getMediaId(), mPlayer.mItem.getMediaId());
+        assertEquals(mediaId, mPlayer.mItem.getMediaId());
     }
 
     /**
@@ -1648,8 +1649,9 @@
     @Test
     public void testGetVideoSize() throws InterruptedException {
         prepareLooper();
+        final MediaItem item = TestUtils.createMediaItemWithMetadata();
         final VideoSize testVideoSize = new VideoSize(100, 42);
-        final CountDownLatch latch = new CountDownLatch(1);
+        final CountDownLatch latch = new CountDownLatch(2);
         final ControllerCallback callback = new ControllerCallback() {
             @Override
             public void onVideoSizeChanged(@NonNull MediaController controller,
@@ -1658,8 +1660,16 @@
                 assertEquals(testVideoSize, videoSize);
                 latch.countDown();
             }
+
+            @Override
+            public void onVideoSizeChanged(@NonNull MediaController controller,
+                    @NonNull VideoSize videoSize) {
+                assertEquals(testVideoSize, videoSize);
+                latch.countDown();
+            }
         };
         MediaController controller = createController(mSession.getToken(), true, null, callback);
+        mPlayer.notifyCurrentMediaItemChanged(item);
         mPlayer.notifyVideoSizeChanged(testVideoSize);
         assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         assertEquals(testVideoSize, controller.getVideoSize());
diff --git a/media2/session/src/androidTest/java/androidx/media2/session/TestBrowserCallback.java b/media2/session/src/androidTest/java/androidx/media2/session/TestBrowserCallback.java
index 0d7bed7..cb17489 100644
--- a/media2/session/src/androidTest/java/androidx/media2/session/TestBrowserCallback.java
+++ b/media2/session/src/androidTest/java/androidx/media2/session/TestBrowserCallback.java
@@ -183,6 +183,12 @@
     }
 
     @Override
+    public void onVideoSizeChanged(@NonNull MediaController controller,
+            @NonNull VideoSize videoSize) {
+        mCallbackProxy.onVideoSizeChanged(controller, videoSize);
+    }
+
+    @Override
     public void onSubtitleData(@NonNull MediaController controller, @NonNull MediaItem item,
             @NonNull SessionPlayer.TrackInfo track, @NonNull SubtitleData data) {
         mCallbackProxy.onSubtitleData(controller, item, track, data);
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaController.java b/media2/session/src/main/java/androidx/media2/session/MediaController.java
index c66a659..4b7cee8 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaController.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaController.java
@@ -823,7 +823,7 @@
             throw new IllegalArgumentException("mediaId shouldn't be empty");
         }
         if (isConnected()) {
-            getImpl().setMediaItem(mediaId);
+            return getImpl().setMediaItem(mediaId);
         }
         return createDisconnectedFuture();
     }
@@ -1034,7 +1034,7 @@
      * <p>
      * This calls {@link SessionPlayer#skipToPlaylistItem(int)}.
      *
-     * @param index The item in the playlist you want to play
+     * @param index The index of the item you want to play in the playlist
      */
     @NonNull
     public ListenableFuture<SessionResult> skipToPlaylistItem(@IntRange(from = 0) int index) {
@@ -1877,21 +1877,32 @@
         public void onPlaybackCompleted(@NonNull MediaController controller) {}
 
         /**
-         * Called when video size is changed.
-         *
-         * @param controller the controller for this event
-         * @param item the media item for which the video size changed
-         * @param videoSize the size of video
-         *
+         * @deprecated Use {@link #onVideoSizeChanged(MediaController, VideoSize)} instead.
          * @hide
          */
         @RestrictTo(LIBRARY_GROUP)
+        @Deprecated
         public void onVideoSizeChanged(@NonNull MediaController controller, @NonNull MediaItem item,
                 @NonNull VideoSize videoSize) {}
 
         /**
-         * Called when the tracks are first retrieved after media is prepared or when new tracks are
-         * found during playback.
+         * Called when video size is changed.
+         *
+         * @param controller the controller for this event
+         * @param videoSize the size of video
+         *
+         * @hide
+         */
+        // TODO: Unhide this (b/134749006)
+        @RestrictTo(LIBRARY_GROUP)
+        public void onVideoSizeChanged(@NonNull MediaController controller,
+                @NonNull VideoSize videoSize) {}
+
+        /**
+         * Called when the tracks of the current media item is changed such as
+         * 1) when tracks of a media item become available,
+         * 2) when new tracks are found during playback, or
+         * 3) when the current media item is changed.
          * <p>
          * When it's called, you should invalidate previous track information and use the new
          * tracks to call {@link #selectTrack(TrackInfo)} or
@@ -1905,7 +1916,7 @@
          * @see TrackInfo#MEDIA_TRACK_TYPE_METADATA
          *
          * @param controller the controller for this event
-         * @param trackInfos the list of track infos
+         * @param trackInfos the list of tracks. It can be empty.
          * @hide
          */
         @RestrictTo(LIBRARY_GROUP)
@@ -1966,7 +1977,7 @@
     }
 
     /**
-     * Holds information about the the way volume is handled for this session.
+     * Holds information about the way volume is handled for this session.
      */
     // The same as MediaController.PlaybackInfo
     @VersionedParcelize
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaControllerImplBase.java b/media2/session/src/main/java/androidx/media2/session/MediaControllerImplBase.java
index 755a17e..3ceb640 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaControllerImplBase.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaControllerImplBase.java
@@ -1144,9 +1144,11 @@
         });
     }
 
-    void notifyVideoSizeChanged(final MediaItem item, final VideoSize videoSize) {
+    void notifyVideoSizeChanged(final VideoSize videoSize) {
+        final MediaItem currentItem;
         synchronized (mLock) {
             mVideoSize = videoSize;
+            currentItem = mCurrentMediaItem;
         }
         mInstance.notifyControllerCallback(new ControllerCallbackRunnable() {
             @Override
@@ -1154,7 +1156,11 @@
                 if (!mInstance.isConnected()) {
                     return;
                 }
-                callback.onVideoSizeChanged(mInstance, item, videoSize);
+
+                if (currentItem != null) {
+                    callback.onVideoSizeChanged(mInstance, currentItem, videoSize);
+                }
+                callback.onVideoSizeChanged(mInstance, videoSize);
             }
         });
     }
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaControllerImplLegacy.java b/media2/session/src/main/java/androidx/media2/session/MediaControllerImplLegacy.java
index e1144e6..625be61 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaControllerImplLegacy.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaControllerImplLegacy.java
@@ -672,18 +672,17 @@
     }
 
     @Override
-    public ListenableFuture<SessionResult> replacePlaylistItem(int index,
-            @NonNull String mediaId) {
+    public ListenableFuture<SessionResult> replacePlaylistItem(int index, @NonNull String mediaId) {
         synchronized (mLock) {
             if (!mConnected) {
                 Log.w(TAG, "Session isn't active", new IllegalStateException());
                 return createFutureWithResult(RESULT_ERROR_SESSION_DISCONNECTED);
             }
-            if (mPlaylist == null || index < 0 || mPlaylist.size() <= index) {
+            if (mQueue == null || index < 0 || index >= mQueue.size()) {
                 return createFutureWithResult(RESULT_ERROR_BAD_VALUE);
             }
-            removePlaylistItem(index);
-            addPlaylistItem(index, mediaId);
+            mControllerCompat.removeQueueItem(mQueue.get(index).getDescription());
+            mControllerCompat.addQueueItem(MediaUtils.createMediaDescriptionCompat(mediaId), index);
         }
         return createFutureWithResult(RESULT_SUCCESS);
     }
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaControllerStub.java b/media2/session/src/main/java/androidx/media2/session/MediaControllerStub.java
index 142dca2..8e16d21 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaControllerStub.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaControllerStub.java
@@ -242,23 +242,18 @@
 
     @Override
     public void onVideoSizeChanged(int seq, final ParcelImpl item, final ParcelImpl videoSize) {
-        if (item == null || videoSize == null) {
+        if (videoSize == null) {
             return;
         }
         dispatchControllerTask(new ControllerTask() {
             @Override
             public void run(MediaControllerImplBase controller) {
-                MediaItem itemObj = MediaParcelUtils.fromParcelable(item);
-                if (itemObj == null) {
-                    Log.w(TAG, "onVideoSizeChanged(): Ignoring null MediaItem");
-                    return;
-                }
                 VideoSize size = MediaParcelUtils.fromParcelable(videoSize);
                 if (size == null) {
                     Log.w(TAG, "onVideoSizeChanged(): Ignoring null VideoSize");
                     return;
                 }
-                controller.notifyVideoSizeChanged(itemObj, size);
+                controller.notifyVideoSizeChanged(size);
             }
         });
     }
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaLibraryServiceLegacyStub.java b/media2/session/src/main/java/androidx/media2/session/MediaLibraryServiceLegacyStub.java
index 062046a..7244f81 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaLibraryServiceLegacyStub.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaLibraryServiceLegacyStub.java
@@ -492,8 +492,7 @@
         }
 
         @Override
-        final void onVideoSizeChanged(int seq, @NonNull MediaItem item,
-                @NonNull VideoSize videoSize) {
+        void onVideoSizeChanged(int seq, @NonNull VideoSize videoSize) throws RemoteException {
             // No-op. BrowserCompat doesn't understand Controller features.
         }
 
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 c6f997e..9895f8c 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaSession.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaSession.java
@@ -95,7 +95,7 @@
  * session.
  * <p>
  * When a session receive transport control commands, the session sends the commands directly to
- * the the underlying media player set by {@link Builder} or {@link #updatePlayer}.
+ * the underlying media player set by {@link Builder} or {@link #updatePlayer}.
  * <p>
  * When an app is finished performing playback it must call {@link #close()} to clean up the session
  * and notify any controllers. The app is responsible for closing the underlying player after
@@ -386,10 +386,12 @@
     }
 
     /**
-     * @hide
-     * @return Bundle
+     * 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
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @NonNull
     public MediaSessionCompat getSessionCompat() {
         return mImpl.getSessionCompat();
     }
@@ -1201,8 +1203,8 @@
                 int currentIdx, int previousIdx, int nextIdx) throws RemoteException;
         abstract void onPlaybackCompleted(int seq) throws RemoteException;
         abstract void onDisconnected(int seq) throws RemoteException;
-        abstract void onVideoSizeChanged(int seq, @NonNull MediaItem item,
-                @NonNull VideoSize videoSize) throws RemoteException;
+        abstract void onVideoSizeChanged(int seq, @NonNull VideoSize videoSize)
+                throws RemoteException;
         abstract void onTrackInfoChanged(int seq, List<TrackInfo> trackInfos,
                 TrackInfo selectedVideoTrack, TrackInfo selectedAudioTrack,
                 TrackInfo selectedSubtitleTrack, TrackInfo selectedMetadataTrack)
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaSessionImplBase.java b/media2/session/src/main/java/androidx/media2/session/MediaSessionImplBase.java
index 04fd45a..eee1bea 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaSessionImplBase.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaSessionImplBase.java
@@ -290,11 +290,15 @@
                     new VolumeProviderCompat(remotePlayer.getVolumeControlType(),
                             remotePlayer.getMaxVolume(),
                             remotePlayer.getVolume()) {
+                        // TODO(b/138091975) Do not ignore the returned Future.
+                        @SuppressWarnings("FutureReturnValueIgnored")
                         @Override
                         public void onSetVolumeTo(int volume) {
                             remotePlayer.setVolume(volume);
                         }
 
+                        // TODO(b/138091975) Do not ignore the returned Future.
+                        @SuppressWarnings("FutureReturnValueIgnored")
                         @Override
                         public void onAdjustVolume(int direction) {
                             remotePlayer.adjustVolume(direction);
@@ -1532,17 +1536,22 @@
         }
 
         @Override
-        public void onVideoSizeChangedInternal(@NonNull final SessionPlayer player,
-                @NonNull final MediaItem item, @NonNull final VideoSize videoSize) {
+        public void onVideoSizeChanged(@NonNull SessionPlayer player, @NonNull VideoSize size) {
             dispatchRemoteControllerTask(player, new RemoteControllerTask() {
                 @Override
                 public void run(ControllerCb callback, int seq) throws RemoteException {
-                    callback.onVideoSizeChanged(seq, item, videoSize);
+                    callback.onVideoSizeChanged(seq, size);
                 }
             });
         }
 
         @Override
+        public void onVideoSizeChangedInternal(@NonNull final SessionPlayer player,
+                @NonNull final MediaItem item, @NonNull final VideoSize videoSize) {
+            onVideoSizeChanged(player, videoSize);
+        }
+
+        @Override
         public void onTrackInfoChanged(@NonNull SessionPlayer player,
                 @NonNull final List<TrackInfo> trackInfos) {
             final MediaSessionImplBase session = getSession();
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaSessionLegacyStub.java b/media2/session/src/main/java/androidx/media2/session/MediaSessionLegacyStub.java
index 016d316..4776445 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaSessionLegacyStub.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaSessionLegacyStub.java
@@ -121,6 +121,8 @@
     @Override
     public void onPrepare() {
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_PREPARE, new SessionTask() {
+            // TODO(b/138091975) Do not ignore the returned Future.
+            @SuppressWarnings("FutureReturnValueIgnored")
             @Override
             public void run(ControllerInfo controller) throws RemoteException {
                 mSessionImpl.prepare();
@@ -180,6 +182,8 @@
     @Override
     public void onPlay() {
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_PLAY, new SessionTask() {
+            // TODO(b/138091975) Do not ignore the returned Future.
+            @SuppressWarnings("FutureReturnValueIgnored")
             @Override
             public void run(ControllerInfo controller) throws RemoteException {
                 mSessionImpl.play();
@@ -239,6 +243,8 @@
     @Override
     public void onPause() {
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_PAUSE, new SessionTask() {
+            // TODO(b/138091975) Do not ignore the returned Future.
+            @SuppressWarnings("FutureReturnValueIgnored")
             @Override
             public void run(ControllerInfo controller) throws RemoteException {
                 mSessionImpl.pause();
@@ -256,6 +262,8 @@
             public void run(ControllerInfo controller) throws RemoteException {
                 handleTaskOnExecutor(controller, null,
                         SessionCommand.COMMAND_CODE_PLAYER_SEEK_TO, new SessionTask() {
+                            // TODO(b/138091975) Do not ignore the returned Future.
+                            @SuppressWarnings("FutureReturnValueIgnored")
                             @Override
                             public void run(ControllerInfo controller) throws RemoteException {
                                 mSessionImpl.pause();
@@ -269,6 +277,8 @@
     @Override
     public void onSeekTo(final long pos) {
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SEEK_TO, new SessionTask() {
+            // TODO(b/138091975) Do not ignore the returned Future.
+            @SuppressWarnings("FutureReturnValueIgnored")
             @Override
             public void run(ControllerInfo controller) throws RemoteException {
                 mSessionImpl.seekTo(pos);
@@ -280,6 +290,8 @@
     public void onSkipToNext() {
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SKIP_TO_NEXT_PLAYLIST_ITEM,
                 new SessionTask() {
+                    // TODO(b/138091975) Do not ignore the returned Future.
+                    @SuppressWarnings("FutureReturnValueIgnored")
                     @Override
                     public void run(ControllerInfo controller) throws RemoteException {
                         mSessionImpl.skipToNextItem();
@@ -291,6 +303,8 @@
     public void onSkipToPrevious() {
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SKIP_TO_PREVIOUS_PLAYLIST_ITEM,
                 new SessionTask() {
+                    // TODO(b/138091975) Do not ignore the returned Future.
+                    @SuppressWarnings("FutureReturnValueIgnored")
                     @Override
                     public void run(ControllerInfo controller) throws RemoteException {
                         mSessionImpl.skipToPreviousItem();
@@ -301,6 +315,8 @@
     @Override
     public void onSetPlaybackSpeed(final float speed) {
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SET_SPEED, new SessionTask() {
+            // TODO(b/138091975) Do not ignore the returned Future.
+            @SuppressWarnings("FutureReturnValueIgnored")
             @Override
             public void run(ControllerInfo controller) throws RemoteException {
                 mSessionImpl.setPlaybackSpeed(speed);
@@ -312,6 +328,8 @@
     public void onSkipToQueueItem(final long queueId) {
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM,
                 new SessionTask() {
+                    // TODO(b/138091975) Do not ignore the returned Future.
+                    @SuppressWarnings("FutureReturnValueIgnored")
                     @Override
                     public void run(ControllerInfo controller) throws RemoteException {
                         List<MediaItem> playlist = mSessionImpl.getPlayer().getPlaylist();
@@ -388,6 +406,8 @@
     public void onSetRepeatMode(final int repeatMode) {
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SET_REPEAT_MODE,
                 new SessionTask() {
+                    // TODO(b/138091975) Do not ignore the returned Future.
+                    @SuppressWarnings("FutureReturnValueIgnored")
                     @Override
                     public void run(ControllerInfo controller) throws RemoteException {
                         mSessionImpl.setRepeatMode(repeatMode);
@@ -399,6 +419,8 @@
     public void onSetShuffleMode(final int shuffleMode) {
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SET_SHUFFLE_MODE,
                 new SessionTask() {
+                    // TODO(b/138091975) Do not ignore the returned Future.
+                    @SuppressWarnings("FutureReturnValueIgnored")
                     @Override
                     public void run(ControllerInfo controller) throws RemoteException {
                         mSessionImpl.setShuffleMode(shuffleMode);
@@ -418,6 +440,8 @@
         }
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_ADD_PLAYLIST_ITEM,
                 new SessionTask() {
+                    // TODO(b/138091975) Do not ignore the returned Future.
+                    @SuppressWarnings("FutureReturnValueIgnored")
                     @Override
                     public void run(ControllerInfo controller) throws RemoteException {
                         String mediaId = description.getMediaId();
@@ -439,6 +463,8 @@
         }
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM,
                 new SessionTask() {
+                    // TODO(b/138091975) Do not ignore the returned Future.
+                    @SuppressWarnings("FutureReturnValueIgnored")
                     @Override
                     public void run(ControllerInfo controller) throws RemoteException {
                         String mediaId = description.getMediaId();
@@ -462,6 +488,8 @@
     public void onRemoveQueueItemAt(final int index) {
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM,
                 new SessionTask() {
+                    // TODO(b/138091975) Do not ignore the returned Future.
+                    @SuppressWarnings("FutureReturnValueIgnored")
                     @Override
                     public void run(ControllerInfo controller) throws RemoteException {
                         if (index < 0) {
@@ -704,7 +732,7 @@
         }
 
         @Override
-        void onVideoSizeChanged(int seq, @NonNull MediaItem item, @NonNull VideoSize videoSize) {
+        void onVideoSizeChanged(int seq, @NonNull VideoSize videoSize) throws RemoteException {
             // no-op
         }
 
@@ -919,7 +947,7 @@
         }
 
         @Override
-        void onVideoSizeChanged(int seq, @NonNull MediaItem item, @NonNull VideoSize videoSize) {
+        void onVideoSizeChanged(int seq, @NonNull VideoSize videoSize) throws RemoteException {
             // no-op
         }
 
diff --git a/media2/session/src/main/java/androidx/media2/session/MediaSessionStub.java b/media2/session/src/main/java/androidx/media2/session/MediaSessionStub.java
index 0507f33..6833d59 100644
--- a/media2/session/src/main/java/androidx/media2/session/MediaSessionStub.java
+++ b/media2/session/src/main/java/androidx/media2/session/MediaSessionStub.java
@@ -1432,11 +1432,9 @@
         }
 
         @Override
-        void onVideoSizeChanged(int seq, @NonNull MediaItem item, @NonNull VideoSize videoSize)
-                throws RemoteException {
-            ParcelImpl itemParcel = MediaParcelUtils.toParcelable(item);
+        void onVideoSizeChanged(int seq, @NonNull VideoSize videoSize) throws RemoteException {
             ParcelImpl videoSizeParcel = MediaParcelUtils.toParcelable(videoSize);
-            mIControllerCallback.onVideoSizeChanged(seq, itemParcel, videoSizeParcel);
+            mIControllerCallback.onVideoSizeChanged(seq, null, videoSizeParcel);
         }
 
         @Override
diff --git a/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/MediaControllerCallbackTest.java b/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/MediaControllerCallbackTest.java
index 8674218..9230345 100644
--- a/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/MediaControllerCallbackTest.java
+++ b/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/MediaControllerCallbackTest.java
@@ -865,7 +865,7 @@
         prepareLooper();
 
         final VideoSize testSize = new VideoSize(100, 42);
-        final CountDownLatch latch = new CountDownLatch(1);
+        final CountDownLatch latch = new CountDownLatch(2);
         final MediaController.ControllerCallback callback =
                 new MediaController.ControllerCallback() {
                     @Override
@@ -875,10 +875,18 @@
                         assertEquals(testSize, videoSize);
                         latch.countDown();
                     }
+
+                    @Override
+                    public void onVideoSizeChanged(@NonNull MediaController controller,
+                            @NonNull VideoSize videoSize) {
+                        assertEquals(testSize, videoSize);
+                        latch.countDown();
+                    }
                 };
 
         MediaController controller = createController(mRemoteSession2.getToken(), true, null,
                 callback);
+        mRemoteSession2.getMockPlayer().notifyCurrentMediaItemChanged(INDEX_FOR_UNKONWN_ITEM);
         mRemoteSession2.getMockPlayer().notifyVideoSizeChanged(testSize);
         assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
     }
diff --git a/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/TestBrowserCallback.java b/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/TestBrowserCallback.java
index b7d38fb..70073cd 100644
--- a/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/TestBrowserCallback.java
+++ b/media2/session/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/tests/TestBrowserCallback.java
@@ -189,6 +189,12 @@
     }
 
     @Override
+    public void onVideoSizeChanged(@NonNull MediaController controller,
+            @NonNull VideoSize videoSize) {
+        mCallbackProxy.onVideoSizeChanged(controller, videoSize);
+    }
+
+    @Override
     public void onTrackInfoChanged(@NonNull MediaController controller,
             @NonNull List<SessionPlayer.TrackInfo> trackInfos) {
         mCallbackProxy.onTrackInfoChanged(controller, trackInfos);
diff --git a/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java b/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java
index eeee0e8..cbf4673 100644
--- a/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java
+++ b/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/MockPlayer.java
@@ -622,6 +622,7 @@
                 @Override
                 public void run() {
                     callback.onVideoSizeChangedInternal(MockPlayer.this, dummyItem, videoSize);
+                    callback.onVideoSizeChanged(MockPlayer.this, videoSize);
                 }
             });
         }
diff --git a/media2/widget/OWNERS b/media2/widget/OWNERS
deleted file mode 100644
index b2c39ea..0000000
--- a/media2/widget/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-akersten@google.com
-dwkang@google.com
-gyumin@google.com
-hdmoon@google.com
-insun@google.com
-jaewan@google.com
-jinpark@google.com
-sungsoo@google.com
diff --git a/media2/widget/build.gradle b/media2/widget/build.gradle
index 2eb0e7f4..19cc8f8 100644
--- a/media2/widget/build.gradle
+++ b/media2/widget/build.gradle
@@ -46,6 +46,9 @@
     defaultConfig {
         minSdkVersion 19
     }
+    sourceSets {
+        main.res.srcDirs += 'src/main/res-public'
+    }
     lintOptions {
 	// Lint cannot determine the groupId of androidx.media2:media2widget,
 	// so it fails on calls to other media2 libraries.
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java
index d0c7292..e0ddf03 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java
@@ -125,7 +125,7 @@
                     latchForPlayingState.countDown();
                 }
             }
-        }, mFileSchemeMediaItem);
+        }, mFileSchemeMediaItem, null);
         setPlayerWrapper(playerWrapper);
         assertTrue(latchForPausedState.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
         onView(allOf(withId(R.id.pause), isCompletelyDisplayed()))
@@ -162,7 +162,7 @@
                 }
                 mState = state;
             }
-        }, mFileSchemeMediaItem);
+        }, mFileSchemeMediaItem, null);
         assertTrue(latchForPreparedState.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
         playerWrapper.play();
         assertTrue(latchForPlayingState.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
@@ -178,7 +178,7 @@
     @Test
     public void testSetPlayerAndController_MultipleTimes() throws Throwable {
         DefaultPlayerCallback callback1 = new DefaultPlayerCallback();
-        PlayerWrapper wrapper1 = createPlayerWrapper(callback1, mFileSchemeMediaItem);
+        PlayerWrapper wrapper1 = createPlayerWrapper(callback1, mFileSchemeMediaItem, null);
         assertTrue(callback1.mPausedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
 
         DefaultPlayerCallback callback2 = new DefaultPlayerCallback();
@@ -191,7 +191,7 @@
         assertTrue(callback3.mPausedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
 
         DefaultPlayerCallback callback4 = new DefaultPlayerCallback();
-        PlayerWrapper wrapper4 = createPlayerWrapper(callback4, mFileSchemeMediaItem);
+        PlayerWrapper wrapper4 = createPlayerWrapper(callback4, mFileSchemeMediaItem, null);
         assertTrue(callback4.mPausedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
 
         setPlayerWrapper(wrapper1);
@@ -244,7 +244,7 @@
                     latchForPausedState.countDown();
                 }
             }
-        }, mFileSchemeMediaItem);
+        }, mFileSchemeMediaItem, null);
         setPlayerWrapper(playerWrapper);
         assertTrue(latchForPausedState.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
         onView(allOf(withId(R.id.ffwd), isCompletelyDisplayed())).perform(click());
@@ -285,7 +285,7 @@
             private boolean equalsSeekPosition(long expected, long actual, long delta) {
                 return (actual < expected + delta) && (actual > expected - delta);
             }
-        }, mFileSchemeMediaItem);
+        }, mFileSchemeMediaItem, null);
         setPlayerWrapper(playerWrapper);
         assertTrue(latchForFfwd.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
         onView(allOf(withId(R.id.rew), isCompletelyDisplayed())).perform(click());
@@ -293,6 +293,40 @@
     }
 
     @Test
+    public void testPrevNextButtonClick() throws Throwable {
+        DefaultPlayerCallback callback = new DefaultPlayerCallback();
+        final List<MediaItem> playlist = createTestPlaylist();
+        final PlayerWrapper playerWrapper = createPlayerWrapper(callback, null, playlist);
+        setPlayerWrapper(playerWrapper);
+
+        assertTrue(callback.mItemLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertEquals(0, playerWrapper.getCurrentMediaItemIndex());
+        onView(allOf(withId(R.id.prev), not(isClickable())));
+        onView(allOf(withId(R.id.next), isClickable()));
+        callback.mItemLatch = new CountDownLatch(1);
+        onView(allOf(withId(R.id.next), isCompletelyDisplayed())).perform(click());
+
+        assertTrue(callback.mItemLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertEquals(1, playerWrapper.getCurrentMediaItemIndex());
+        onView(allOf(withId(R.id.prev), isClickable()));
+        onView(allOf(withId(R.id.next), isClickable()));
+        callback.mItemLatch = new CountDownLatch(1);
+        onView(allOf(withId(R.id.next), isCompletelyDisplayed())).perform(click());
+
+        assertTrue(callback.mItemLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertEquals(2, playerWrapper.getCurrentMediaItemIndex());
+        onView(allOf(withId(R.id.prev), isClickable()));
+        onView(allOf(withId(R.id.next), not(isClickable())));
+        callback.mItemLatch = new CountDownLatch(1);
+        onView(allOf(withId(R.id.prev), isCompletelyDisplayed())).perform(click());
+
+        assertTrue(callback.mItemLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        assertEquals(1, playerWrapper.getCurrentMediaItemIndex());
+        onView(allOf(withId(R.id.prev), isClickable()));
+        onView(allOf(withId(R.id.next), isClickable()));
+    }
+
+    @Test
     public void testSetMetadataForNonMusicFile() throws Throwable {
         final String title = "BigBuckBunny";
         final CountDownLatch latch = new CountDownLatch(1);
@@ -309,7 +343,7 @@
                     latch.countDown();
                 }
             }
-        }, mFileSchemeMediaItem);
+        }, mFileSchemeMediaItem, null);
         setPlayerWrapper(playerWrapper);
         assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
         onView(withId(R.id.title_text)).check(matches(withText(title)));
@@ -325,10 +359,15 @@
         final PlayerWrapper playerWrapper = createPlayerWrapper(new PlayerWrapper.PlayerCallback() {
             @Override
             public void onTrackInfoChanged(@NonNull PlayerWrapper player,
-                    @NonNull List<TrackInfo> trackInfos) {
+                    @NonNull List<TrackInfo> tracks) {
+                assertNotNull(tracks);
+                if (tracks.isEmpty()) {
+                    // This callback can be called before tracks are available after setMediaItem
+                    return;
+                }
                 latch.countDown();
             }
-        }, uriMediaItem);
+        }, uriMediaItem, null);
         setPlayerWrapper(playerWrapper);
         assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
         onView(withId(R.id.subtitle)).check(matches(not(isDisplayed())));
@@ -390,7 +429,7 @@
                 assertEquals(mFirstSubtitleTrack, trackInfo);
                 latchForSubtitleDeselect.countDown();
             }
-        }, mediaItem);
+        }, mediaItem, null);
         setPlayerWrapper(playerWrapper);
         assertTrue(latchForReady.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
         // MediaPlayer needs a surface to be set in order to produce subtitle tracks
@@ -425,7 +464,7 @@
                     latchForPlayingState.countDown();
                 }
             }
-        }, mFileSchemeMediaItem);
+        }, mFileSchemeMediaItem, null);
         mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
@@ -464,7 +503,7 @@
     }
 
     private PlayerWrapper createPlayerWrapper(@NonNull PlayerWrapper.PlayerCallback callback,
-            @Nullable MediaItem item) {
-        return createPlayerWrapperOfType(callback, item, null, mPlayerType);
+            @Nullable MediaItem item, @Nullable List<MediaItem> playlist) {
+        return createPlayerWrapperOfType(callback, item, playlist, mPlayerType);
     }
 }
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaWidgetTestBase.java b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaWidgetTestBase.java
index d7b3eb8..e005a01 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaWidgetTestBase.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaWidgetTestBase.java
@@ -130,12 +130,19 @@
         return new UriMediaItem.Builder(uri).build();
     }
 
+    List<MediaItem> createTestPlaylist() {
+        List<MediaItem> list = new ArrayList<>();
+        list.add(createTestMediaItem(Uri.parse("android.resource://" + mContext.getPackageName()
+                + "/" + R.raw.test_file_scheme_video)));
+        list.add(createTestMediaItem(Uri.parse("android.resource://" + mContext.getPackageName()
+                + "/" + R.raw.test_music)));
+        list.add(createTestMediaItem(Uri.parse("android.resource://" + mContext.getPackageName()
+                + "/" + R.raw.testvideo_with_2_subtitle_tracks)));
+        return list;
+    }
+
     PlayerWrapper createPlayerWrapperOfController(@NonNull PlayerWrapper.PlayerCallback callback,
             @Nullable MediaItem item, @Nullable List<MediaItem> playlist) {
-        if (item == null && playlist == null) {
-            return null;
-        }
-
         prepareLooper();
 
         SessionPlayer player = new MediaPlayer(mContext);
@@ -164,10 +171,6 @@
 
     PlayerWrapper createPlayerWrapperOfPlayer(@NonNull PlayerWrapper.PlayerCallback callback,
             @Nullable MediaItem item, @Nullable List<MediaItem> playlist) {
-        if (item == null && playlist == null) {
-            return null;
-        }
-
         SessionPlayer player = new MediaPlayer(mContext);
         synchronized (mLock) {
             mPlayers.add(player);
@@ -218,7 +221,7 @@
     }
 
     class DefaultPlayerCallback extends PlayerWrapper.PlayerCallback {
-        CountDownLatch mItemLatch = new CountDownLatch(1);
+        volatile CountDownLatch mItemLatch = new CountDownLatch(1);
         CountDownLatch mPausedLatch = new CountDownLatch(1);
         CountDownLatch mPlayingLatch = new CountDownLatch(1);
 
diff --git a/media2/widget/src/main/java/androidx/media2/widget/MediaControlView.java b/media2/widget/src/main/java/androidx/media2/widget/MediaControlView.java
index 94373aa..f3145de 100644
--- a/media2/widget/src/main/java/androidx/media2/widget/MediaControlView.java
+++ b/media2/widget/src/main/java/androidx/media2/widget/MediaControlView.java
@@ -1660,6 +1660,33 @@
         }
     }
 
+    void updatePrevNextButtons(int prevIndex, int nextIndex) {
+        int n = mTransportControlsMap.size();
+        for (int i = 0; i < n; i++) {
+            int sizeType = mTransportControlsMap.keyAt(i);
+            View prevButton = findControlButton(sizeType, R.id.prev);
+            if (prevButton != null) {
+                if (prevIndex > SessionPlayer.INVALID_ITEM_INDEX) {
+                    prevButton.setAlpha(1.0f);
+                    prevButton.setEnabled(true);
+                } else {
+                    prevButton.setAlpha(0.5f);
+                    prevButton.setEnabled(false);
+                }
+            }
+            View nextButton = findControlButton(sizeType, R.id.next);
+            if (nextButton != null) {
+                if (nextIndex > SessionPlayer.INVALID_ITEM_INDEX) {
+                    nextButton.setAlpha(1.0f);
+                    nextButton.setEnabled(true);
+                } else {
+                    nextButton.setAlpha(0.5f);
+                    nextButton.setEnabled(false);
+                }
+            }
+        }
+    }
+
     boolean shouldNotHideBars() {
         return (isCurrentItemMusic() && mSizeType == SIZE_TYPE_FULL)
                 || mAccessibilityManager.isTouchExplorationEnabled()
@@ -2079,6 +2106,20 @@
             }
             updateTimeViews(mediaItem);
             updateTitleView(mediaItem);
+            updatePrevNextButtons(player.getPreviousMediaItemIndex(),
+                    player.getNextMediaItemIndex());
+        }
+
+        @Override
+        void onPlaylistChanged(@NonNull PlayerWrapper player, @Nullable List<MediaItem> list,
+                @Nullable MediaMetadata metadata) {
+            if (player != mPlayer) return;
+
+            if (DEBUG) {
+                Log.d(TAG, "onPlaylistChanged(): list: " + list + ", metadata: " + metadata);
+            }
+            updatePrevNextButtons(player.getPreviousMediaItemIndex(),
+                    player.getNextMediaItemIndex());
         }
 
         @Override
diff --git a/media2/widget/src/main/java/androidx/media2/widget/PlayerWrapper.java b/media2/widget/src/main/java/androidx/media2/widget/PlayerWrapper.java
index 08df637..de3cc59 100644
--- a/media2/widget/src/main/java/androidx/media2/widget/PlayerWrapper.java
+++ b/media2/widget/src/main/java/androidx/media2/widget/PlayerWrapper.java
@@ -195,6 +195,8 @@
                 && mAllowedCommands.hasCommand(SessionCommand.COMMAND_CODE_PLAYER_DESELECT_TRACK);
     }
 
+    // TODO(b/138091975) Do not ignore the returned Future.
+    @SuppressWarnings("FutureReturnValueIgnored")
     void pause() {
         if (mController != null) {
             mController.pause();
@@ -203,6 +205,8 @@
         }
     }
 
+    // TODO(b/138091975) Do not ignore the returned Future.
+    @SuppressWarnings("FutureReturnValueIgnored")
     void play() {
         if (mController != null) {
             mController.play();
@@ -211,6 +215,8 @@
         }
     }
 
+    // TODO(b/138091975) Do not ignore the returned Future.
+    @SuppressWarnings("FutureReturnValueIgnored")
     void seekTo(long posMs) {
         if (mController != null) {
             mController.seekTo(posMs);
@@ -219,6 +225,8 @@
         }
     }
 
+    // TODO(b/138091975) Do not ignore the returned Future.
+    @SuppressWarnings("FutureReturnValueIgnored")
     void skipToNextItem() {
         if (mController != null) {
             mController.skipToNextPlaylistItem();
@@ -227,6 +235,8 @@
         }
     }
 
+    // TODO(b/138091975) Do not ignore the returned Future.
+    @SuppressWarnings("FutureReturnValueIgnored")
     void skipToPreviousItem() {
         if (mController != null) {
             mController.skipToPreviousPlaylistItem();
@@ -244,6 +254,8 @@
         return 1f;
     }
 
+    // TODO(b/138091975) Do not ignore the returned Future.
+    @SuppressWarnings("FutureReturnValueIgnored")
     void setPlaybackSpeed(float speed) {
         if (mController != null) {
             mController.setPlaybackSpeed(speed);
@@ -252,6 +264,8 @@
         }
     }
 
+    // TODO(b/138091975) Do not ignore the returned Future.
+    @SuppressWarnings("FutureReturnValueIgnored")
     void selectTrack(TrackInfo trackInfo) {
         if (mController != null) {
             mController.selectTrack(trackInfo);
@@ -260,6 +274,8 @@
         }
     }
 
+    // TODO(b/138091975) Do not ignore the returned Future.
+    @SuppressWarnings("FutureReturnValueIgnored")
     void deselectTrack(TrackInfo trackInfo) {
         if (mController != null) {
             mController.deselectTrack(trackInfo);
@@ -346,23 +362,11 @@
             mWrapperCallback.onAllowedCommandsChanged(this, allowedCommands);
         }
         mWrapperCallback.onCurrentMediaItemChanged(this, item);
-
-        // notify other non-cached states
-        mWrapperCallback.onPlaybackSpeedChanged(this, getPlaybackSpeed());
-        List<TrackInfo> trackInfos = getTrackInfo();
-        if (trackInfos != null) {
-            mWrapperCallback.onTrackInfoChanged(this, trackInfos);
-        }
-        if (item != null) {
-            mWrapperCallback.onVideoSizeChanged(this, item, getVideoSize());
-        }
+        notifyNonCachedStates();
     }
 
     @NonNull
     VideoSize getVideoSize() {
-        if (mSavedPlayerState == SessionPlayer.PLAYER_STATE_IDLE) {
-            return new VideoSize(0, 0);
-        }
         if (mController != null) {
             return mController.getVideoSize();
         } else if (mPlayer != null) {
@@ -373,9 +377,6 @@
 
     @NonNull
     List<TrackInfo> getTrackInfo() {
-        if (mSavedPlayerState == SessionPlayer.PLAYER_STATE_IDLE) {
-            return null;
-        }
         if (mController != null) {
             return mController.getTrackInfo();
         } else if (mPlayer != null) {
@@ -386,9 +387,6 @@
 
     @Nullable
     TrackInfo getSelectedTrack(int trackType) {
-        if (mSavedPlayerState == SessionPlayer.PLAYER_STATE_IDLE) {
-            return null;
-        }
         if (mController != null) {
             return mController.getSelectedTrack(trackType);
         } else if (mPlayer != null) {
@@ -406,6 +404,33 @@
         return null;
     }
 
+    int getCurrentMediaItemIndex() {
+        if (mController != null) {
+            return mController.getCurrentMediaItemIndex();
+        } else if (mPlayer != null) {
+            return mPlayer.getCurrentMediaItemIndex();
+        }
+        return SessionPlayer.INVALID_ITEM_INDEX;
+    }
+
+    int getPreviousMediaItemIndex() {
+        if (mController != null) {
+            return mController.getPreviousMediaItemIndex();
+        } else if (mPlayer != null) {
+            return mPlayer.getPreviousMediaItemIndex();
+        }
+        return SessionPlayer.INVALID_ITEM_INDEX;
+    }
+
+    int getNextMediaItemIndex() {
+        if (mController != null) {
+            return mController.getNextMediaItemIndex();
+        } else if (mPlayer != null) {
+            return mPlayer.getNextMediaItemIndex();
+        }
+        return SessionPlayer.INVALID_ITEM_INDEX;
+    }
+
     private class MediaControllerCallback extends MediaController.ControllerCallback {
         MediaControllerCallback() {
         }
@@ -450,6 +475,12 @@
         }
 
         @Override
+        public void onPlaylistChanged(@NonNull MediaController controller,
+                @Nullable List<MediaItem> list, @Nullable MediaMetadata metadata) {
+            mWrapperCallback.onPlaylistChanged(PlayerWrapper.this, list, metadata);
+        }
+
+        @Override
         public void onPlaybackCompleted(@NonNull MediaController controller) {
             mWrapperCallback.onPlaybackCompleted(PlayerWrapper.this);
         }
@@ -485,6 +516,19 @@
         }
     }
 
+    private void notifyNonCachedStates() {
+        mWrapperCallback.onPlaybackSpeedChanged(this, getPlaybackSpeed());
+
+        List<TrackInfo> trackInfos = getTrackInfo();
+        if (trackInfos != null) {
+            mWrapperCallback.onTrackInfoChanged(PlayerWrapper.this, trackInfos);
+        }
+        MediaItem item = getCurrentMediaItem();
+        if (item != null) {
+            mWrapperCallback.onVideoSizeChanged(PlayerWrapper.this, item, getVideoSize());
+        }
+    }
+
     private class SessionPlayerCallback extends SessionPlayer.PlayerCallback {
         SessionPlayerCallback() {
         }
@@ -514,6 +558,12 @@
         }
 
         @Override
+        public void onPlaylistChanged(@NonNull SessionPlayer player, @Nullable List<MediaItem> list,
+                @Nullable MediaMetadata metadata) {
+            mWrapperCallback.onPlaylistChanged(PlayerWrapper.this, list, metadata);
+        }
+
+        @Override
         public void onPlaybackCompleted(@NonNull SessionPlayer player) {
             mWrapperCallback.onPlaybackCompleted(PlayerWrapper.this);
         }
@@ -555,6 +605,9 @@
         }
         void onCurrentMediaItemChanged(@NonNull PlayerWrapper player, @Nullable MediaItem item) {
         }
+        void onPlaylistChanged(@NonNull PlayerWrapper player, @Nullable List<MediaItem> list,
+                @Nullable MediaMetadata metadata) {
+        }
         void onPlayerStateChanged(@NonNull PlayerWrapper player, int state) {
         }
         void onPlaybackSpeedChanged(@NonNull PlayerWrapper player, float speed) {
diff --git a/media2/widget/src/main/res/values/public.xml b/media2/widget/src/main/res-public/values/public_attrs.xml
similarity index 93%
rename from media2/widget/src/main/res/values/public.xml
rename to media2/widget/src/main/res-public/values/public_attrs.xml
index 6f7f00f..c0bcefd 100644
--- a/media2/widget/src/main/res/values/public.xml
+++ b/media2/widget/src/main/res-public/values/public_attrs.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright 2018 The Android Open Source Project
+  ~ 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.
diff --git a/navigation/benchmark/build.gradle b/navigation/benchmark/build.gradle
index 66e8c38..ad5d976 100644
--- a/navigation/benchmark/build.gradle
+++ b/navigation/benchmark/build.gradle
@@ -22,10 +22,11 @@
     id("AndroidXPlugin")
     id("com.android.library")
     id("kotlin-android")
+    id("androidx.benchmark")
 }
 
 dependencies {
-    androidTestImplementation(project(":benchmark"))
+    androidTestImplementation(project(":benchmark:benchmark-junit4"))
     androidTestImplementation(project(":navigation:navigation-runtime"))
     androidTestImplementation(project(":navigation:navigation-testing"))
     androidTestImplementation(JUNIT)
diff --git a/navigation/benchmark/src/androidTest/java/androidx/navigation/NavInflaterBenchmark.kt b/navigation/benchmark/src/androidTest/java/androidx/navigation/NavInflaterBenchmark.kt
index 09b55d4..9ff4684 100644
--- a/navigation/benchmark/src/androidTest/java/androidx/navigation/NavInflaterBenchmark.kt
+++ b/navigation/benchmark/src/androidTest/java/androidx/navigation/NavInflaterBenchmark.kt
@@ -16,8 +16,8 @@
 
 package androidx.navigation
 
-import androidx.benchmark.BenchmarkRule
-import androidx.benchmark.measureRepeated
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
 import androidx.navigation.testing.TestNavigatorProvider
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
diff --git a/navigation/navigation-runtime/api/2.1.0-rc01.ignore b/navigation/navigation-runtime/api/2.1.0-rc01.ignore
new file mode 100644
index 0000000..1ca5fd9
--- /dev/null
+++ b/navigation/navigation-runtime/api/2.1.0-rc01.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedDeprecatedMethod: androidx.navigation.NavController#getViewModelStore(int):
+    Removed deprecated method androidx.navigation.NavController.getViewModelStore(int)
diff --git a/navigation/navigation-runtime/api/2.2.0-alpha01.txt b/navigation/navigation-runtime/api/2.2.0-alpha01.txt
index f361023..5db4937 100644
--- a/navigation/navigation-runtime/api/2.2.0-alpha01.txt
+++ b/navigation/navigation-runtime/api/2.2.0-alpha01.txt
@@ -46,7 +46,6 @@
     method public androidx.navigation.NavGraph getGraph();
     method public androidx.navigation.NavInflater getNavInflater();
     method public androidx.navigation.NavigatorProvider getNavigatorProvider();
-    method @Deprecated public androidx.lifecycle.ViewModelStore getViewModelStore(@IdRes int);
     method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int);
     method public boolean handleDeepLink(android.content.Intent?);
     method public void navigate(@IdRes int);
diff --git a/navigation/navigation-runtime/api/current.txt b/navigation/navigation-runtime/api/current.txt
index f361023..5db4937 100644
--- a/navigation/navigation-runtime/api/current.txt
+++ b/navigation/navigation-runtime/api/current.txt
@@ -46,7 +46,6 @@
     method public androidx.navigation.NavGraph getGraph();
     method public androidx.navigation.NavInflater getNavInflater();
     method public androidx.navigation.NavigatorProvider getNavigatorProvider();
-    method @Deprecated public androidx.lifecycle.ViewModelStore getViewModelStore(@IdRes int);
     method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int);
     method public boolean handleDeepLink(android.content.Intent?);
     method public void navigate(@IdRes int);
diff --git a/navigation/navigation-runtime/api/restricted_2.1.0-rc01.ignore b/navigation/navigation-runtime/api/restricted_2.1.0-rc01.ignore
new file mode 100644
index 0000000..1ca5fd9
--- /dev/null
+++ b/navigation/navigation-runtime/api/restricted_2.1.0-rc01.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedDeprecatedMethod: androidx.navigation.NavController#getViewModelStore(int):
+    Removed deprecated method androidx.navigation.NavController.getViewModelStore(int)
diff --git a/navigation/navigation-runtime/api/restricted_2.1.0-rc01.txt b/navigation/navigation-runtime/api/restricted_2.1.0-rc01.txt
new file mode 100644
index 0000000..5db4937
--- /dev/null
+++ b/navigation/navigation-runtime/api/restricted_2.1.0-rc01.txt
@@ -0,0 +1,117 @@
+// Signature format: 3.0
+package androidx.navigation {
+
+  @androidx.navigation.Navigator.Name("activity") public class ActivityNavigator extends androidx.navigation.Navigator<androidx.navigation.ActivityNavigator.Destination> {
+    ctor public ActivityNavigator(android.content.Context);
+    method public static void applyPopAnimationsToPendingTransition(android.app.Activity);
+    method public androidx.navigation.ActivityNavigator.Destination createDestination();
+    method public androidx.navigation.NavDestination? navigate(androidx.navigation.ActivityNavigator.Destination, android.os.Bundle?, androidx.navigation.NavOptions?, androidx.navigation.Navigator.Extras?);
+    method public boolean popBackStack();
+  }
+
+  @androidx.navigation.NavDestination.ClassType(Activity.class) public static class ActivityNavigator.Destination extends androidx.navigation.NavDestination {
+    ctor public ActivityNavigator.Destination(androidx.navigation.NavigatorProvider);
+    ctor public ActivityNavigator.Destination(androidx.navigation.Navigator<? extends androidx.navigation.ActivityNavigator.Destination>);
+    method public final String? getAction();
+    method public final android.content.ComponentName? getComponent();
+    method public final android.net.Uri? getData();
+    method public final String? getDataPattern();
+    method public final android.content.Intent? getIntent();
+    method public final String? getTargetPackage();
+    method public final androidx.navigation.ActivityNavigator.Destination setAction(String?);
+    method public final androidx.navigation.ActivityNavigator.Destination setComponentName(android.content.ComponentName?);
+    method public final androidx.navigation.ActivityNavigator.Destination setData(android.net.Uri?);
+    method public final androidx.navigation.ActivityNavigator.Destination setDataPattern(String?);
+    method public final androidx.navigation.ActivityNavigator.Destination setIntent(android.content.Intent?);
+    method public final androidx.navigation.ActivityNavigator.Destination setTargetPackage(String?);
+  }
+
+  public static final class ActivityNavigator.Extras implements androidx.navigation.Navigator.Extras {
+    method public androidx.core.app.ActivityOptionsCompat? getActivityOptions();
+    method public int getFlags();
+  }
+
+  public static final class ActivityNavigator.Extras.Builder {
+    ctor public ActivityNavigator.Extras.Builder();
+    method public androidx.navigation.ActivityNavigator.Extras.Builder addFlags(int);
+    method public androidx.navigation.ActivityNavigator.Extras build();
+    method public androidx.navigation.ActivityNavigator.Extras.Builder setActivityOptions(androidx.core.app.ActivityOptionsCompat);
+  }
+
+  public class NavController {
+    ctor public NavController(android.content.Context);
+    method public void addOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener);
+    method public androidx.navigation.NavDeepLinkBuilder createDeepLink();
+    method public androidx.navigation.NavDestination? getCurrentDestination();
+    method public androidx.navigation.NavGraph getGraph();
+    method public androidx.navigation.NavInflater getNavInflater();
+    method public androidx.navigation.NavigatorProvider getNavigatorProvider();
+    method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int);
+    method public boolean handleDeepLink(android.content.Intent?);
+    method public void navigate(@IdRes int);
+    method public void navigate(@IdRes int, android.os.Bundle?);
+    method public void navigate(@IdRes int, android.os.Bundle?, androidx.navigation.NavOptions?);
+    method public void navigate(@IdRes int, android.os.Bundle?, androidx.navigation.NavOptions?, androidx.navigation.Navigator.Extras?);
+    method public void navigate(android.net.Uri);
+    method public void navigate(android.net.Uri, androidx.navigation.NavOptions?);
+    method public void navigate(android.net.Uri, androidx.navigation.NavOptions?, androidx.navigation.Navigator.Extras?);
+    method public void navigate(androidx.navigation.NavDirections);
+    method public void navigate(androidx.navigation.NavDirections, androidx.navigation.NavOptions?);
+    method public void navigate(androidx.navigation.NavDirections, androidx.navigation.Navigator.Extras);
+    method public boolean navigateUp();
+    method public boolean popBackStack();
+    method public boolean popBackStack(@IdRes int, boolean);
+    method public void removeOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener);
+    method @CallSuper public void restoreState(android.os.Bundle?);
+    method @CallSuper public android.os.Bundle? saveState();
+    method @CallSuper public void setGraph(@NavigationRes int);
+    method @CallSuper public void setGraph(@NavigationRes int, android.os.Bundle?);
+    method @CallSuper public void setGraph(androidx.navigation.NavGraph);
+    method @CallSuper public void setGraph(androidx.navigation.NavGraph, android.os.Bundle?);
+    field public static final String KEY_DEEP_LINK_INTENT = "android-support-nav:controller:deepLinkIntent";
+  }
+
+  public static interface NavController.OnDestinationChangedListener {
+    method public void onDestinationChanged(androidx.navigation.NavController, androidx.navigation.NavDestination, android.os.Bundle?);
+  }
+
+  public final class NavDeepLinkBuilder {
+    ctor public NavDeepLinkBuilder(android.content.Context);
+    method public android.app.PendingIntent createPendingIntent();
+    method public androidx.core.app.TaskStackBuilder createTaskStackBuilder();
+    method public androidx.navigation.NavDeepLinkBuilder setArguments(android.os.Bundle?);
+    method public androidx.navigation.NavDeepLinkBuilder setComponentName(Class<? extends android.app.Activity>);
+    method public androidx.navigation.NavDeepLinkBuilder setComponentName(android.content.ComponentName);
+    method public androidx.navigation.NavDeepLinkBuilder setDestination(@IdRes int);
+    method public androidx.navigation.NavDeepLinkBuilder setGraph(@NavigationRes int);
+    method public androidx.navigation.NavDeepLinkBuilder setGraph(androidx.navigation.NavGraph);
+  }
+
+  public interface NavHost {
+    method public androidx.navigation.NavController getNavController();
+  }
+
+  public final class NavHostController extends androidx.navigation.NavController {
+    ctor public NavHostController(android.content.Context);
+    method public void enableOnBackPressed(boolean);
+    method public void setLifecycleOwner(androidx.lifecycle.LifecycleOwner);
+    method public void setOnBackPressedDispatcher(androidx.activity.OnBackPressedDispatcher);
+    method public void setViewModelStore(androidx.lifecycle.ViewModelStore);
+  }
+
+  public final class NavInflater {
+    ctor public NavInflater(android.content.Context, androidx.navigation.NavigatorProvider);
+    method public androidx.navigation.NavGraph inflate(@NavigationRes int);
+  }
+
+  public final class Navigation {
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int);
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(@IdRes int, android.os.Bundle?);
+    method public static android.view.View.OnClickListener createNavigateOnClickListener(androidx.navigation.NavDirections);
+    method public static androidx.navigation.NavController findNavController(android.app.Activity, @IdRes int);
+    method public static androidx.navigation.NavController findNavController(android.view.View);
+    method public static void setViewNavController(android.view.View, androidx.navigation.NavController?);
+  }
+
+}
+
diff --git a/navigation/navigation-runtime/api/restricted_2.2.0-alpha01.txt b/navigation/navigation-runtime/api/restricted_2.2.0-alpha01.txt
index f361023..5db4937 100644
--- a/navigation/navigation-runtime/api/restricted_2.2.0-alpha01.txt
+++ b/navigation/navigation-runtime/api/restricted_2.2.0-alpha01.txt
@@ -46,7 +46,6 @@
     method public androidx.navigation.NavGraph getGraph();
     method public androidx.navigation.NavInflater getNavInflater();
     method public androidx.navigation.NavigatorProvider getNavigatorProvider();
-    method @Deprecated public androidx.lifecycle.ViewModelStore getViewModelStore(@IdRes int);
     method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int);
     method public boolean handleDeepLink(android.content.Intent?);
     method public void navigate(@IdRes int);
diff --git a/navigation/navigation-runtime/api/restricted_current.txt b/navigation/navigation-runtime/api/restricted_current.txt
index f361023..5db4937 100644
--- a/navigation/navigation-runtime/api/restricted_current.txt
+++ b/navigation/navigation-runtime/api/restricted_current.txt
@@ -46,7 +46,6 @@
     method public androidx.navigation.NavGraph getGraph();
     method public androidx.navigation.NavInflater getNavInflater();
     method public androidx.navigation.NavigatorProvider getNavigatorProvider();
-    method @Deprecated public androidx.lifecycle.ViewModelStore getViewModelStore(@IdRes int);
     method public androidx.lifecycle.ViewModelStoreOwner getViewModelStoreOwner(@IdRes int);
     method public boolean handleDeepLink(android.content.Intent?);
     method public void navigate(@IdRes int);
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 e20b2c8..3b2698d 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
@@ -16,11 +16,14 @@
 
 package androidx.navigation
 
+import android.app.Application
 import android.content.Context
 import android.net.Uri
 import android.os.Bundle
 import android.os.Parcel
 import android.os.Parcelable
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelStore
 import androidx.navigation.test.R
 import androidx.navigation.testing.TestNavigator
@@ -890,6 +893,25 @@
     }
 
     @Test
+    fun testGetViewModelStoreOwnerAndroidViewModel() {
+        val navController = createNavController()
+        navController.setViewModelStore(ViewModelStore())
+        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[TestAndroidViewModel::class.java]
+        assertThat(viewModel).isNotNull()
+    }
+
+    @Test
     fun testSaveRestoreGetViewModelStoreOwner() {
         val hostStore = ViewModelStore()
         val navController = createNavController()
@@ -966,6 +988,8 @@
     }
 }
 
+class TestAndroidViewModel(application: Application) : AndroidViewModel(application)
+
 /**
  * [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 8aaa08c..0b9c4e5d 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntry.java
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntry.java
@@ -16,10 +16,14 @@
 
 package androidx.navigation;
 
+import android.app.Application;
+import android.content.Context;
 import android.os.Bundle;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.lifecycle.HasDefaultViewModelProviderFactory;
+import androidx.lifecycle.ViewModelProvider;
 import androidx.lifecycle.ViewModelStore;
 import androidx.lifecycle.ViewModelStoreOwner;
 
@@ -28,7 +32,8 @@
 /**
  * Representation of an entry in the back stack of a {@link NavController}.
  */
-final class NavBackStackEntry implements ViewModelStoreOwner {
+final class NavBackStackEntry implements ViewModelStoreOwner, HasDefaultViewModelProviderFactory {
+    private final Context mContext;
     private final NavDestination mDestination;
     private final Bundle mArgs;
 
@@ -37,13 +42,17 @@
     final UUID mId;
     private NavControllerViewModel mNavControllerViewModel;
 
-    NavBackStackEntry(@NonNull NavDestination destination, @Nullable Bundle args,
+    NavBackStackEntry(@NonNull Context context,
+            @NonNull NavDestination destination, @Nullable Bundle args,
             @Nullable NavControllerViewModel navControllerViewModel) {
-        this(UUID.randomUUID(), destination, args, navControllerViewModel);
+        this(context, destination, args, navControllerViewModel, UUID.randomUUID());
     }
 
-    NavBackStackEntry(@NonNull UUID uuid, @NonNull NavDestination destination,
-            @Nullable Bundle args, @Nullable NavControllerViewModel navControllerViewModel) {
+    NavBackStackEntry(@NonNull Context context,
+            @NonNull NavDestination destination, @Nullable Bundle args,
+            @Nullable NavControllerViewModel navControllerViewModel,
+            @NonNull UUID uuid) {
+        mContext = context;
         mId = uuid;
         mDestination = destination;
         mArgs = args;
@@ -77,4 +86,11 @@
     public ViewModelStore getViewModelStore() {
         return mNavControllerViewModel.getViewModelStore(mId);
     }
+
+    @NonNull
+    @Override
+    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
+        return ViewModelProvider.AndroidViewModelFactory.getInstance(
+                (Application) mContext.getApplicationContext());
+    }
 }
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.java b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.java
new file mode 100644
index 0000000..a39a6e1
--- /dev/null
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.java
@@ -0,0 +1,87 @@
+/*
+ * 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.navigation;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.UUID;
+
+@SuppressLint("BanParcelableUsage")
+final class NavBackStackEntryState implements Parcelable {
+
+    private final UUID mUUID;
+    private final int mDestinationId;
+    private final Bundle mArgs;
+
+    NavBackStackEntryState(NavBackStackEntry entry) {
+        mUUID = entry.mId;
+        mDestinationId = entry.getDestination().getId();
+        mArgs = entry.getArguments();
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    NavBackStackEntryState(Parcel in) {
+        mUUID = UUID.fromString(in.readString());
+        mDestinationId = in.readInt();
+        mArgs = in.readBundle(getClass().getClassLoader());
+    }
+
+    @NonNull
+    UUID getUUID() {
+        return mUUID;
+    }
+
+    int getDestinationId() {
+        return mDestinationId;
+    }
+
+    @Nullable
+    Bundle getArgs() {
+        return mArgs;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int i) {
+        parcel.writeString(mUUID.toString());
+        parcel.writeInt(mDestinationId);
+        parcel.writeBundle(mArgs);
+    }
+
+    public static final Parcelable.Creator<NavBackStackEntryState> CREATOR =
+            new Parcelable.Creator<NavBackStackEntryState>() {
+                @Override
+                public NavBackStackEntryState createFromParcel(Parcel in) {
+                    return new NavBackStackEntryState(in);
+                }
+
+                @Override
+                public NavBackStackEntryState[] newArray(int size) {
+                    return new NavBackStackEntryState[size];
+                }
+            };
+}
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 f06382e..112e24e 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.java
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.java
@@ -42,7 +42,6 @@
 import java.util.Deque;
 import java.util.Iterator;
 import java.util.Map;
-import java.util.UUID;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
@@ -64,11 +63,8 @@
             "android-support-nav:controller:navigatorState";
     private static final String KEY_NAVIGATOR_STATE_NAMES =
             "android-support-nav:controller:navigatorState:names";
-    private static final String KEY_BACK_STACK_UUIDS =
-            "android-support-nav:controller:backStackUUIDs";
-    private static final String KEY_BACK_STACK_IDS = "android-support-nav:controller:backStackIds";
-    private static final String KEY_BACK_STACK_ARGS =
-            "android-support-nav:controller:backStackArgs";
+    private static final String KEY_BACK_STACK =
+            "android-support-nav:controller:backStack";
     static final String KEY_DEEP_LINK_IDS = "android-support-nav:controller:deepLinkIds";
     static final String KEY_DEEP_LINK_EXTRAS =
             "android-support-nav:controller:deepLinkExtras";
@@ -85,9 +81,7 @@
     private NavInflater mInflater;
     private NavGraph mGraph;
     private Bundle mNavigatorStateToRestore;
-    private String[] mBackStackUUIDsToRestore;
-    private int[] mBackStackIdsToRestore;
-    private Parcelable[] mBackStackArgsToRestore;
+    private Parcelable[] mBackStackToRestore;
     private boolean mDeepLinkHandled;
 
     private final Deque<NavBackStackEntry> mBackStack = new ArrayDeque<>();
@@ -474,25 +468,23 @@
                 }
             }
         }
-        if (mBackStackUUIDsToRestore != null) {
-            for (int index = 0; index < mBackStackUUIDsToRestore.length; index++) {
-                UUID uuid = UUID.fromString(mBackStackUUIDsToRestore[index]);
-                int destinationId = mBackStackIdsToRestore[index];
-                Bundle args = (Bundle) mBackStackArgsToRestore[index];
-                NavDestination node = findDestination(destinationId);
+        if (mBackStackToRestore != null) {
+            for (Parcelable parcelable : mBackStackToRestore) {
+                NavBackStackEntryState state = (NavBackStackEntryState) parcelable;
+                NavDestination node = findDestination(state.getDestinationId());
                 if (node == null) {
                     throw new IllegalStateException("unknown destination during restore: "
-                            + mContext.getResources().getResourceName(destinationId));
+                            + mContext.getResources().getResourceName(state.getDestinationId()));
                 }
+                Bundle args = state.getArgs();
                 if (args != null) {
                     args.setClassLoader(mContext.getClassLoader());
                 }
-                mBackStack.add(new NavBackStackEntry(uuid, node, args, mViewModel));
+                mBackStack.add(new NavBackStackEntry(mContext, node, args, mViewModel,
+                        state.getUUID()));
             }
             updateOnBackPressedCallbackEnabled();
-            mBackStackUUIDsToRestore = null;
-            mBackStackIdsToRestore = null;
-            mBackStackArgsToRestore = null;
+            mBackStackToRestore = null;
         }
         if (mGraph != null && mBackStack.isEmpty()) {
             boolean deepLinked = !mDeepLinkHandled && mActivity != null
@@ -873,7 +865,7 @@
             }
             // The mGraph should always be on the back stack after you navigate()
             if (mBackStack.isEmpty()) {
-                mBackStack.add(new NavBackStackEntry(mGraph, finalArgs, mViewModel));
+                mBackStack.add(new NavBackStackEntry(mContext, mGraph, finalArgs, mViewModel));
             }
             // Now ensure all intermediate NavGraphs are put on the back stack
             // to ensure that global actions work.
@@ -882,13 +874,14 @@
             while (destination != null && findDestination(destination.getId()) == null) {
                 NavGraph parent = destination.getParent();
                 if (parent != null) {
-                    hierarchy.addFirst(new NavBackStackEntry(parent, finalArgs, mViewModel));
+                    hierarchy.addFirst(new NavBackStackEntry(mContext, parent, finalArgs,
+                            mViewModel));
                 }
                 destination = parent;
             }
             mBackStack.addAll(hierarchy);
             // And finally, add the new destination with its default args
-            NavBackStackEntry newBackStackEntry = new NavBackStackEntry(newDest,
+            NavBackStackEntry newBackStackEntry = new NavBackStackEntry(mContext, newDest,
                     newDest.addInDefaultArgs(finalArgs), mViewModel);
             mBackStack.add(newBackStackEntry);
         }
@@ -971,18 +964,12 @@
             if (b == null) {
                 b = new Bundle();
             }
-            String[] backStackUUIDs = new String[mBackStack.size()];
-            int[] backStackIds = new int[mBackStack.size()];
-            Parcelable[] backStackArgs = new Parcelable[mBackStack.size()];
+            Parcelable[] backStack = new Parcelable[mBackStack.size()];
             int index = 0;
             for (NavBackStackEntry backStackEntry : mBackStack) {
-                backStackUUIDs[index] = backStackEntry.mId.toString();
-                backStackIds[index] = backStackEntry.getDestination().getId();
-                backStackArgs[index++] = backStackEntry.getArguments();
+                backStack[index++] = new NavBackStackEntryState(backStackEntry);
             }
-            b.putStringArray(KEY_BACK_STACK_UUIDS, backStackUUIDs);
-            b.putIntArray(KEY_BACK_STACK_IDS, backStackIds);
-            b.putParcelableArray(KEY_BACK_STACK_ARGS, backStackArgs);
+            b.putParcelableArray(KEY_BACK_STACK, backStack);
         }
         if (mDeepLinkHandled) {
             if (b == null) {
@@ -1011,9 +998,7 @@
         navState.setClassLoader(mContext.getClassLoader());
 
         mNavigatorStateToRestore = navState.getBundle(KEY_NAVIGATOR_STATE);
-        mBackStackUUIDsToRestore = navState.getStringArray(KEY_BACK_STACK_UUIDS);
-        mBackStackIdsToRestore = navState.getIntArray(KEY_BACK_STACK_IDS);
-        mBackStackArgsToRestore = navState.getParcelableArray(KEY_BACK_STACK_ARGS);
+        mBackStackToRestore = navState.getParcelableArray(KEY_BACK_STACK);
         mDeepLinkHandled = navState.getBoolean(KEY_DEEP_LINK_HANDLED);
     }
 
@@ -1052,26 +1037,6 @@
     }
 
     /**
-     * Gets the {@link ViewModelStore} for a NavGraph.This can be passed to
-     * {@link androidx.lifecycle.ViewModelProvider} to retrieve a ViewModel that is scoped
-     * to the navigation graph - it will be cleared when the navigation graph is popped off
-     * the back stack.
-     *
-     * @param navGraphId ID of a NavGraph that exists on the back stack
-     * @throws IllegalStateException if called before the {@link NavHost} has called
-     * {@link NavHostController#setViewModelStore}.
-     * @throws IllegalArgumentException if the NavGraph is not on the back stack
-     * @deprecated Use {@link #getViewModelStoreOwner(int)}, calling
-     * {@link ViewModelStoreOwner#getViewModelStore()} on the returned ViewModelStoreOwner
-     * if you need specifically a ViewModelStore.
-     */
-    @Deprecated
-    @NonNull
-    public ViewModelStore getViewModelStore(@IdRes int navGraphId) {
-        return getViewModelStoreOwner(navGraphId).getViewModelStore();
-    }
-
-    /**
      * Gets the {@link ViewModelStoreOwner} for a NavGraph.This can be passed to
      * {@link androidx.lifecycle.ViewModelProvider} to retrieve a ViewModel that is scoped
      * to the navigation graph - it will be cleared when the navigation graph is popped off
diff --git a/paging/common/api/3.0.0-alpha01.txt b/paging/common/api/3.0.0-alpha01.txt
index 903b245..0d44da6 100644
--- a/paging/common/api/3.0.0-alpha01.txt
+++ b/paging/common/api/3.0.0-alpha01.txt
@@ -71,7 +71,6 @@
 
   public abstract static class ItemKeyedDataSource.LoadCallback<Value> {
     ctor public ItemKeyedDataSource.LoadCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends Value> data);
   }
 
@@ -111,13 +110,11 @@
 
   public abstract static class PageKeyedDataSource.LoadCallback<Key, Value> {
     ctor public PageKeyedDataSource.LoadCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends Value> data, Key? adjacentPageKey);
   }
 
   public abstract static class PageKeyedDataSource.LoadInitialCallback<Key, Value> {
     ctor public PageKeyedDataSource.LoadInitialCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends Value> data, int position, int totalCount, Key? previousPageKey, Key? nextPageKey);
     method public abstract void onResult(java.util.List<? extends Value> data, Key? previousPageKey, Key? nextPageKey);
   }
@@ -150,7 +147,6 @@
     method public final androidx.paging.PagedSource<?,T> getPagedSource();
     method public int getPositionOffset();
     method public int getSize();
-    method public abstract boolean isContiguous();
     method public abstract boolean isDetached();
     method public boolean isImmutable();
     method public void loadAround(int index);
@@ -160,7 +156,6 @@
     method public java.util.List<T> snapshot();
     property public androidx.paging.PagedList.Config config;
     property @Deprecated public final androidx.paging.DataSource<?,T> dataSource;
-    property public abstract boolean isContiguous;
     property public abstract boolean isDetached;
     property public boolean isImmutable;
     property public abstract Object? lastKey;
@@ -243,18 +238,15 @@
 
   public abstract class PagedSource<Key, Value> {
     ctor public PagedSource();
-    method public abstract boolean getInvalid();
+    method public final boolean getInvalid();
     method public abstract androidx.paging.PagedSource.KeyProvider<Key,Value> getKeyProvider();
-    method public abstract void invalidate();
+    method public void invalidate();
     method public abstract boolean isRetryableError(Throwable error);
     method public abstract suspend Object load(androidx.paging.PagedSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagedSource.LoadResult<Key,Value>> p);
-    property public abstract boolean invalid;
+    method public final void registerInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
+    method public final void unregisterInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
+    property public final boolean invalid;
     property public abstract androidx.paging.PagedSource.KeyProvider<Key,Value> keyProvider;
-    field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
-    field public static final androidx.paging.PagedSource.Companion! Companion;
-  }
-
-  public static final class PagedSource.Companion {
   }
 
   public abstract static sealed class PagedSource.KeyProvider<Key, Value> {
@@ -289,22 +281,25 @@
   }
 
   public static final class PagedSource.LoadResult<Key, Value> {
-    ctor public PagedSource.LoadResult(int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset, boolean counted);
+    ctor public PagedSource.LoadResult(@IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset);
     method public int component1();
     method public int component2();
     method public Key? component3();
     method public Key? component4();
     method public java.util.List<Value> component5();
     method public int component6();
-    method public boolean component7();
-    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset, boolean counted);
-    method public boolean getCounted();
+    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset);
     method public java.util.List<Value> getData();
     method public int getItemsAfter();
     method public int getItemsBefore();
     method public Key? getNextKey();
     method public int getOffset();
     method public Key? getPrevKey();
+    field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
+    field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+  }
+
+  public static final class PagedSource.LoadResult.Companion {
   }
 
   public enum PagedSource.LoadType {
@@ -313,6 +308,10 @@
     enum_constant public static final androidx.paging.PagedSource.LoadType START;
   }
 
+  public final class PagedSourceKt {
+    ctor public PagedSourceKt();
+  }
+
   public abstract class PositionalDataSource<T> extends androidx.paging.DataSource<java.lang.Integer,T> {
     ctor public PositionalDataSource();
     method public static final int computeInitialLoadPosition(androidx.paging.PositionalDataSource.LoadInitialParams params, int totalCount);
@@ -329,7 +328,6 @@
 
   public abstract static class PositionalDataSource.LoadInitialCallback<T> {
     ctor public PositionalDataSource.LoadInitialCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends T> data, int position, int totalCount);
     method public abstract void onResult(java.util.List<? extends T> data, int position);
   }
@@ -344,7 +342,6 @@
 
   public abstract static class PositionalDataSource.LoadRangeCallback<T> {
     ctor public PositionalDataSource.LoadRangeCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends T> data);
   }
 
diff --git a/paging/common/api/api_lint.ignore b/paging/common/api/api_lint.ignore
index a443331..dec4b46 100644
--- a/paging/common/api/api_lint.ignore
+++ b/paging/common/api/api_lint.ignore
@@ -1,22 +1,12 @@
 // Baseline format: 1.0
 DocumentExceptions: androidx.paging.DataSource#getExecutor():
     Method DataSource.getExecutor appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
-DocumentExceptions: androidx.paging.ItemKeyedDataSource.LoadCallback#onError(Throwable):
-    Method LoadCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
-DocumentExceptions: androidx.paging.PageKeyedDataSource.LoadCallback#onError(Throwable):
-    Method LoadCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
-DocumentExceptions: androidx.paging.PageKeyedDataSource.LoadInitialCallback#onError(Throwable):
-    Method LoadInitialCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
 DocumentExceptions: androidx.paging.PagedList#loadAround(int):
     Method PagedList.loadAround 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.PagedList.Config.Builder#build():
     Method Builder.build 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
 DocumentExceptions: androidx.paging.PagedList.Config.Builder#setPageSize(int):
     Method Builder.setPageSize 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
-DocumentExceptions: androidx.paging.PositionalDataSource.LoadInitialCallback#onError(Throwable):
-    Method LoadInitialCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
-DocumentExceptions: androidx.paging.PositionalDataSource.LoadRangeCallback#onError(Throwable):
-    Method LoadRangeCallback.onError 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
 
 
 EqualsAndHashCode: androidx.paging.DataSource.BaseResult#equals(Object):
diff --git a/paging/common/api/current.txt b/paging/common/api/current.txt
index 903b245..0d44da6 100644
--- a/paging/common/api/current.txt
+++ b/paging/common/api/current.txt
@@ -71,7 +71,6 @@
 
   public abstract static class ItemKeyedDataSource.LoadCallback<Value> {
     ctor public ItemKeyedDataSource.LoadCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends Value> data);
   }
 
@@ -111,13 +110,11 @@
 
   public abstract static class PageKeyedDataSource.LoadCallback<Key, Value> {
     ctor public PageKeyedDataSource.LoadCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends Value> data, Key? adjacentPageKey);
   }
 
   public abstract static class PageKeyedDataSource.LoadInitialCallback<Key, Value> {
     ctor public PageKeyedDataSource.LoadInitialCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends Value> data, int position, int totalCount, Key? previousPageKey, Key? nextPageKey);
     method public abstract void onResult(java.util.List<? extends Value> data, Key? previousPageKey, Key? nextPageKey);
   }
@@ -150,7 +147,6 @@
     method public final androidx.paging.PagedSource<?,T> getPagedSource();
     method public int getPositionOffset();
     method public int getSize();
-    method public abstract boolean isContiguous();
     method public abstract boolean isDetached();
     method public boolean isImmutable();
     method public void loadAround(int index);
@@ -160,7 +156,6 @@
     method public java.util.List<T> snapshot();
     property public androidx.paging.PagedList.Config config;
     property @Deprecated public final androidx.paging.DataSource<?,T> dataSource;
-    property public abstract boolean isContiguous;
     property public abstract boolean isDetached;
     property public boolean isImmutable;
     property public abstract Object? lastKey;
@@ -243,18 +238,15 @@
 
   public abstract class PagedSource<Key, Value> {
     ctor public PagedSource();
-    method public abstract boolean getInvalid();
+    method public final boolean getInvalid();
     method public abstract androidx.paging.PagedSource.KeyProvider<Key,Value> getKeyProvider();
-    method public abstract void invalidate();
+    method public void invalidate();
     method public abstract boolean isRetryableError(Throwable error);
     method public abstract suspend Object load(androidx.paging.PagedSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagedSource.LoadResult<Key,Value>> p);
-    property public abstract boolean invalid;
+    method public final void registerInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
+    method public final void unregisterInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
+    property public final boolean invalid;
     property public abstract androidx.paging.PagedSource.KeyProvider<Key,Value> keyProvider;
-    field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
-    field public static final androidx.paging.PagedSource.Companion! Companion;
-  }
-
-  public static final class PagedSource.Companion {
   }
 
   public abstract static sealed class PagedSource.KeyProvider<Key, Value> {
@@ -289,22 +281,25 @@
   }
 
   public static final class PagedSource.LoadResult<Key, Value> {
-    ctor public PagedSource.LoadResult(int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset, boolean counted);
+    ctor public PagedSource.LoadResult(@IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset);
     method public int component1();
     method public int component2();
     method public Key? component3();
     method public Key? component4();
     method public java.util.List<Value> component5();
     method public int component6();
-    method public boolean component7();
-    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset, boolean counted);
-    method public boolean getCounted();
+    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset);
     method public java.util.List<Value> getData();
     method public int getItemsAfter();
     method public int getItemsBefore();
     method public Key? getNextKey();
     method public int getOffset();
     method public Key? getPrevKey();
+    field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
+    field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+  }
+
+  public static final class PagedSource.LoadResult.Companion {
   }
 
   public enum PagedSource.LoadType {
@@ -313,6 +308,10 @@
     enum_constant public static final androidx.paging.PagedSource.LoadType START;
   }
 
+  public final class PagedSourceKt {
+    ctor public PagedSourceKt();
+  }
+
   public abstract class PositionalDataSource<T> extends androidx.paging.DataSource<java.lang.Integer,T> {
     ctor public PositionalDataSource();
     method public static final int computeInitialLoadPosition(androidx.paging.PositionalDataSource.LoadInitialParams params, int totalCount);
@@ -329,7 +328,6 @@
 
   public abstract static class PositionalDataSource.LoadInitialCallback<T> {
     ctor public PositionalDataSource.LoadInitialCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends T> data, int position, int totalCount);
     method public abstract void onResult(java.util.List<? extends T> data, int position);
   }
@@ -344,7 +342,6 @@
 
   public abstract static class PositionalDataSource.LoadRangeCallback<T> {
     ctor public PositionalDataSource.LoadRangeCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends T> data);
   }
 
diff --git a/paging/common/api/restricted_3.0.0-alpha01.txt b/paging/common/api/restricted_3.0.0-alpha01.txt
index ccaf366..73c6e1a 100644
--- a/paging/common/api/restricted_3.0.0-alpha01.txt
+++ b/paging/common/api/restricted_3.0.0-alpha01.txt
@@ -75,7 +75,6 @@
 
   public abstract static class ItemKeyedDataSource.LoadCallback<Value> {
     ctor public ItemKeyedDataSource.LoadCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends Value> data);
   }
 
@@ -115,13 +114,11 @@
 
   public abstract static class PageKeyedDataSource.LoadCallback<Key, Value> {
     ctor public PageKeyedDataSource.LoadCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends Value> data, Key? adjacentPageKey);
   }
 
   public abstract static class PageKeyedDataSource.LoadInitialCallback<Key, Value> {
     ctor public PageKeyedDataSource.LoadInitialCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends Value> data, int position, int totalCount, Key? previousPageKey, Key? nextPageKey);
     method public abstract void onResult(java.util.List<? extends Value> data, Key? previousPageKey, Key? nextPageKey);
   }
@@ -156,7 +153,6 @@
     method public int getPositionOffset();
     method public int getSize();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final androidx.paging.PagedStorage<T> getStorage();
-    method public abstract boolean isContiguous();
     method public abstract boolean isDetached();
     method public boolean isImmutable();
     method public void loadAround(int index);
@@ -167,7 +163,6 @@
     method public java.util.List<T> snapshot();
     property public androidx.paging.PagedList.Config config;
     property @Deprecated public final androidx.paging.DataSource<?,T> dataSource;
-    property public abstract boolean isContiguous;
     property public abstract boolean isDetached;
     property public boolean isImmutable;
     property public abstract Object? lastKey;
@@ -266,18 +261,15 @@
 
   public abstract class PagedSource<Key, Value> {
     ctor public PagedSource();
-    method public abstract boolean getInvalid();
+    method public final boolean getInvalid();
     method public abstract androidx.paging.PagedSource.KeyProvider<Key,Value> getKeyProvider();
-    method public abstract void invalidate();
+    method public void invalidate();
     method public abstract boolean isRetryableError(Throwable error);
     method public abstract suspend Object load(androidx.paging.PagedSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagedSource.LoadResult<Key,Value>> p);
-    property public abstract boolean invalid;
+    method public final void registerInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
+    method public final void unregisterInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
+    property public final boolean invalid;
     property public abstract androidx.paging.PagedSource.KeyProvider<Key,Value> keyProvider;
-    field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
-    field public static final androidx.paging.PagedSource.Companion! Companion;
-  }
-
-  public static final class PagedSource.Companion {
   }
 
   public abstract static sealed class PagedSource.KeyProvider<Key, Value> {
@@ -312,22 +304,25 @@
   }
 
   public static final class PagedSource.LoadResult<Key, Value> {
-    ctor public PagedSource.LoadResult(int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset, boolean counted);
+    ctor public PagedSource.LoadResult(@IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset);
     method public int component1();
     method public int component2();
     method public Key? component3();
     method public Key? component4();
     method public java.util.List<Value> component5();
     method public int component6();
-    method public boolean component7();
-    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset, boolean counted);
-    method public boolean getCounted();
+    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset);
     method public java.util.List<Value> getData();
     method public int getItemsAfter();
     method public int getItemsBefore();
     method public Key? getNextKey();
     method public int getOffset();
     method public Key? getPrevKey();
+    field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
+    field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+  }
+
+  public static final class PagedSource.LoadResult.Companion {
   }
 
   public enum PagedSource.LoadType {
@@ -336,14 +331,15 @@
     enum_constant public static final androidx.paging.PagedSource.LoadType START;
   }
 
+  public final class PagedSourceKt {
+    ctor public PagedSourceKt();
+  }
+
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class PagedSourceWrapper<Key, Value> extends androidx.paging.PagedSource<Key,Value> {
     ctor public PagedSourceWrapper(internal androidx.paging.DataSource<Key,Value> dataSource);
-    method public boolean getInvalid();
     method public androidx.paging.PagedSource.KeyProvider<Key,Value> getKeyProvider();
-    method public void invalidate();
     method public boolean isRetryableError(Throwable error);
     method public suspend Object load(androidx.paging.PagedSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagedSource.LoadResult<Key,Value>> p);
-    property public boolean invalid;
     property public androidx.paging.PagedSource.KeyProvider<Key,Value> keyProvider;
   }
 
@@ -366,7 +362,6 @@
 
   public abstract static class PositionalDataSource.LoadInitialCallback<T> {
     ctor public PositionalDataSource.LoadInitialCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends T> data, int position, int totalCount);
     method public abstract void onResult(java.util.List<? extends T> data, int position);
   }
@@ -381,7 +376,6 @@
 
   public abstract static class PositionalDataSource.LoadRangeCallback<T> {
     ctor public PositionalDataSource.LoadRangeCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends T> data);
   }
 
diff --git a/paging/common/api/restricted_current.txt b/paging/common/api/restricted_current.txt
index ccaf366..73c6e1a 100644
--- a/paging/common/api/restricted_current.txt
+++ b/paging/common/api/restricted_current.txt
@@ -75,7 +75,6 @@
 
   public abstract static class ItemKeyedDataSource.LoadCallback<Value> {
     ctor public ItemKeyedDataSource.LoadCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends Value> data);
   }
 
@@ -115,13 +114,11 @@
 
   public abstract static class PageKeyedDataSource.LoadCallback<Key, Value> {
     ctor public PageKeyedDataSource.LoadCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends Value> data, Key? adjacentPageKey);
   }
 
   public abstract static class PageKeyedDataSource.LoadInitialCallback<Key, Value> {
     ctor public PageKeyedDataSource.LoadInitialCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends Value> data, int position, int totalCount, Key? previousPageKey, Key? nextPageKey);
     method public abstract void onResult(java.util.List<? extends Value> data, Key? previousPageKey, Key? nextPageKey);
   }
@@ -156,7 +153,6 @@
     method public int getPositionOffset();
     method public int getSize();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final androidx.paging.PagedStorage<T> getStorage();
-    method public abstract boolean isContiguous();
     method public abstract boolean isDetached();
     method public boolean isImmutable();
     method public void loadAround(int index);
@@ -167,7 +163,6 @@
     method public java.util.List<T> snapshot();
     property public androidx.paging.PagedList.Config config;
     property @Deprecated public final androidx.paging.DataSource<?,T> dataSource;
-    property public abstract boolean isContiguous;
     property public abstract boolean isDetached;
     property public boolean isImmutable;
     property public abstract Object? lastKey;
@@ -266,18 +261,15 @@
 
   public abstract class PagedSource<Key, Value> {
     ctor public PagedSource();
-    method public abstract boolean getInvalid();
+    method public final boolean getInvalid();
     method public abstract androidx.paging.PagedSource.KeyProvider<Key,Value> getKeyProvider();
-    method public abstract void invalidate();
+    method public void invalidate();
     method public abstract boolean isRetryableError(Throwable error);
     method public abstract suspend Object load(androidx.paging.PagedSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagedSource.LoadResult<Key,Value>> p);
-    property public abstract boolean invalid;
+    method public final void registerInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
+    method public final void unregisterInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit> onInvalidatedCallback);
+    property public final boolean invalid;
     property public abstract androidx.paging.PagedSource.KeyProvider<Key,Value> keyProvider;
-    field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
-    field public static final androidx.paging.PagedSource.Companion! Companion;
-  }
-
-  public static final class PagedSource.Companion {
   }
 
   public abstract static sealed class PagedSource.KeyProvider<Key, Value> {
@@ -312,22 +304,25 @@
   }
 
   public static final class PagedSource.LoadResult<Key, Value> {
-    ctor public PagedSource.LoadResult(int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset, boolean counted);
+    ctor public PagedSource.LoadResult(@IntRange(from=null) int itemsBefore, @IntRange(from=null) int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset);
     method public int component1();
     method public int component2();
     method public Key? component3();
     method public Key? component4();
     method public java.util.List<Value> component5();
     method public int component6();
-    method public boolean component7();
-    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset, boolean counted);
-    method public boolean getCounted();
+    method public androidx.paging.PagedSource.LoadResult<Key,Value> copy(int itemsBefore, int itemsAfter, Key? nextKey, Key? prevKey, java.util.List<? extends Value> data, int offset);
     method public java.util.List<Value> getData();
     method public int getItemsAfter();
     method public int getItemsBefore();
     method public Key? getNextKey();
     method public int getOffset();
     method public Key? getPrevKey();
+    field public static final int COUNT_UNDEFINED = -1; // 0xffffffff
+    field public static final androidx.paging.PagedSource.LoadResult.Companion! Companion;
+  }
+
+  public static final class PagedSource.LoadResult.Companion {
   }
 
   public enum PagedSource.LoadType {
@@ -336,14 +331,15 @@
     enum_constant public static final androidx.paging.PagedSource.LoadType START;
   }
 
+  public final class PagedSourceKt {
+    ctor public PagedSourceKt();
+  }
+
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class PagedSourceWrapper<Key, Value> extends androidx.paging.PagedSource<Key,Value> {
     ctor public PagedSourceWrapper(internal androidx.paging.DataSource<Key,Value> dataSource);
-    method public boolean getInvalid();
     method public androidx.paging.PagedSource.KeyProvider<Key,Value> getKeyProvider();
-    method public void invalidate();
     method public boolean isRetryableError(Throwable error);
     method public suspend Object load(androidx.paging.PagedSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagedSource.LoadResult<Key,Value>> p);
-    property public boolean invalid;
     property public androidx.paging.PagedSource.KeyProvider<Key,Value> keyProvider;
   }
 
@@ -366,7 +362,6 @@
 
   public abstract static class PositionalDataSource.LoadInitialCallback<T> {
     ctor public PositionalDataSource.LoadInitialCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends T> data, int position, int totalCount);
     method public abstract void onResult(java.util.List<? extends T> data, int position);
   }
@@ -381,7 +376,6 @@
 
   public abstract static class PositionalDataSource.LoadRangeCallback<T> {
     ctor public PositionalDataSource.LoadRangeCallback();
-    method public void onError(Throwable error);
     method public abstract void onResult(java.util.List<? extends T> data);
   }
 
diff --git a/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt b/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
index c80e97d..1cde4c0 100644
--- a/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/ContiguousPagedList.kt
@@ -18,8 +18,8 @@
 
 import androidx.annotation.MainThread
 import androidx.annotation.RestrictTo
-import androidx.paging.PagedSource.Companion.COUNT_UNDEFINED
 import androidx.paging.PagedSource.KeyProvider
+import androidx.paging.PagedSource.LoadResult.Companion.COUNT_UNDEFINED
 import kotlinx.coroutines.CoroutineScope
 import java.util.concurrent.Executor
 
@@ -84,8 +84,6 @@
     override val isDetached
         get() = pager.isDetached
 
-    override val isContiguous = true
-
     override val lastKey
         get() = when (val keyProvider = pagedSource.keyProvider) {
             is KeyProvider.Positional -> {
@@ -207,9 +205,9 @@
         if (config.enablePlaceholders) {
             // Placeholders enabled, pass raw data to storage init
             storage.init(
-                initialResult.itemsBefore,
+                if (initialResult.itemsBefore != COUNT_UNDEFINED) initialResult.itemsBefore else 0,
                 initialResult.data,
-                initialResult.itemsAfter,
+                if (initialResult.itemsAfter != COUNT_UNDEFINED) initialResult.itemsAfter else 0,
                 initialResult.offset,
                 this
             )
@@ -220,7 +218,7 @@
                 0,
                 initialResult.data,
                 0,
-                initialResult.offset + initialResult.itemsBefore,
+                initialResult.itemsBefore + initialResult.offset,
                 this
             )
         }
@@ -228,10 +226,9 @@
         if (this.lastLoad == LAST_LOAD_UNSPECIFIED) {
             // Because the ContiguousPagedList wasn't initialized with a last load position,
             // initialize it to the middle of the initial load
-
             val itemsBefore =
                 if (initialResult.itemsBefore != COUNT_UNDEFINED) initialResult.itemsBefore else 0
-            this.lastLoad = (itemsBefore + initialResult.data.size / 2)
+            this.lastLoad = itemsBefore + initialResult.data.size / 2
         }
         triggerBoundaryCallback(LoadType.REFRESH, initialResult.data)
     }
diff --git a/paging/common/src/main/kotlin/androidx/paging/DataSource.kt b/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
index 7992157..5330acb 100644
--- a/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/DataSource.kt
@@ -21,6 +21,7 @@
 import androidx.annotation.VisibleForTesting
 import androidx.annotation.WorkerThread
 import androidx.arch.core.util.Function
+import androidx.paging.PagedSource.LoadResult.Companion.COUNT_UNDEFINED
 import java.util.concurrent.CopyOnWriteArrayList
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicBoolean
@@ -521,13 +522,12 @@
          */
         @Suppress("UNCHECKED_CAST") // Guaranteed to be the correct Key type.
         internal fun <Key : Any> toLoadResult() = PagedSource.LoadResult(
-            leadingNulls,
-            trailingNulls,
+            if (counted) leadingNulls else COUNT_UNDEFINED,
+            if (counted) trailingNulls else COUNT_UNDEFINED,
             nextKey as Key?,
             prevKey as Key?,
             data,
-            offset,
-            counted
+            offset
         )
 
         override fun equals(other: Any?) = when (other) {
diff --git a/paging/common/src/main/kotlin/androidx/paging/ItemKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/ItemKeyedDataSource.kt
index 5acb252..969e289 100644
--- a/paging/common/src/main/kotlin/androidx/paging/ItemKeyedDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/ItemKeyedDataSource.kt
@@ -23,7 +23,6 @@
 import kotlinx.coroutines.CancellableContinuation
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
-import kotlin.coroutines.resumeWithException
 
 /**
  * Incremental data loader for paging keyed content, where loaded content uses previously loaded
@@ -177,21 +176,6 @@
          * @param data List of items loaded from the [ItemKeyedDataSource].
          */
         abstract fun onResult(data: List<Value>)
-
-        /**
-         * Called to report an error from a DataSource.
-         *
-         * Call this method to report an error from [loadInitial], [loadBefore], or [loadAfter]
-         * methods.
-         *
-         * @param error The error that occurred during loading.
-         */
-        open fun onError(error: Throwable) {
-            // TODO: remove default implementation in 3.0
-            throw IllegalStateException(
-                "You must implement onError if implementing your own load callback"
-            )
-        }
     }
 
     @Suppress("RedundantVisibilityModifier") // Metalava doesn't inherit visibility properly.
@@ -220,10 +204,6 @@
                 override fun onResult(data: List<Value>) {
                     cont.resume(InitialResult(data))
                 }
-
-                override fun onError(error: Throwable) {
-                    cont.resumeWithException(error)
-                }
             })
         }
 
@@ -346,8 +326,4 @@
         override fun onResult(data: List<Value>) {
             resume(Result(data))
         }
-
-        override fun onError(error: Throwable) {
-            resumeWithException(error)
-        }
     }
diff --git a/paging/common/src/main/kotlin/androidx/paging/PageKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/PageKeyedDataSource.kt
index 4d133ac..25d5f3e 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PageKeyedDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PageKeyedDataSource.kt
@@ -22,7 +22,6 @@
 import kotlinx.coroutines.CancellableContinuation
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
-import kotlin.coroutines.resumeWithException
 
 /**
  * Incremental data loader for page-keyed content, where requests return keys for next/previous
@@ -170,20 +169,6 @@
          * can be loaded after.
          */
         abstract fun onResult(data: List<Value>, previousPageKey: Key?, nextPageKey: Key?)
-
-        /**
-         * Called to report an error from a DataSource.
-         *
-         * Call this method to report an error from [loadInitial].
-         *
-         * @param error The error that occurred during loading.
-         */
-        open fun onError(error: Throwable) {
-            // TODO: remove default implementation in 3.0
-            throw IllegalStateException(
-                "You must implement onError if implementing your own load callback"
-            )
-        }
     }
 
     /**
@@ -219,21 +204,6 @@
          * direction.
          */
         abstract fun onResult(data: List<Value>, adjacentPageKey: Key?)
-
-        /**
-         * Called to report an error from a DataSource.
-         *
-         * Call this method to report an error from your PageKeyedDataSource's [loadBefore] and
-         * [loadAfter] methods.
-         *
-         * @param error The error that occurred during loading.
-         */
-        open fun onError(error: Throwable) {
-            // TODO: remove default implementation in 3.0
-            throw IllegalStateException(
-                "You must implement onError if implementing your own load callback"
-            )
-        }
     }
 
     /**
@@ -271,10 +241,6 @@
                 override fun onResult(data: List<Value>, previousPageKey: Key?, nextPageKey: Key?) {
                     cont.resume(InitialResult(data, previousPageKey, nextPageKey))
                 }
-
-                override fun onError(error: Throwable) {
-                    cont.resumeWithException(error)
-                }
             })
         }
 
@@ -377,8 +343,4 @@
         override fun onResult(data: List<Value>, adjacentPageKey: Key?) {
             resume(Result(data, adjacentPageKey))
         }
-
-        override fun onError(error: Throwable) {
-            resumeWithException(error)
-        }
     }
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
index 6cd5774..6d56579 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedList.kt
@@ -20,7 +20,6 @@
 import androidx.annotation.IntRange
 import androidx.annotation.MainThread
 import androidx.annotation.RestrictTo
-import androidx.annotation.VisibleForTesting
 import androidx.annotation.WorkerThread
 import androidx.paging.PagedList.Callback
 import androidx.paging.PagedList.Config
@@ -1004,9 +1003,6 @@
     override val size
         get() = storage.size
 
-    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
-    abstract val isContiguous: Boolean
-
     /**
      * The [PagedSource] that provides data to this [PagedList].
      */
@@ -1131,8 +1127,8 @@
      * Retry any retryable errors associated with this [PagedList].
      *
      * If for example a network [PagedSource] append timed out, calling this method will retry the
-     * failed append load. Note that your [PagedSource] will need to pass `true` to `onError()` to
-     * signify the error as retryable.
+     * failed append load. Note that your [PagedSource] will need to implement
+     * [PagedSource.isRetryableError] to return `true` for errors that are retryable.
      *
      * You can observe loading state via [addWeakLoadStateListener], though generally this is done
      * through the [PagedListAdapter][androidx.paging.PagedListAdapter] or
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedSource.kt b/paging/common/src/main/kotlin/androidx/paging/PagedSource.kt
index a5b495d..90542b1 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedSource.kt
@@ -16,10 +16,24 @@
 
 package androidx.paging
 
+import androidx.annotation.IntRange
 import androidx.paging.PagedSource.KeyProvider
 import androidx.paging.PagedSource.KeyProvider.ItemKey
 import androidx.paging.PagedSource.KeyProvider.PageKey
 import androidx.paging.PagedSource.KeyProvider.Positional
+import java.util.concurrent.CopyOnWriteArrayList
+import java.util.concurrent.atomic.AtomicBoolean
+
+/**
+ * Factory for [PagedSource]s.
+ *
+ * Data-loading systems of an application or library can implement provide this type to allow
+ * `LiveData<PagedList>`s to be created.
+ *
+ * @param Key Key identifying items in PagedSource.
+ * @param Value Type of items in the list loaded by the PagedSources.
+ */
+typealias PagedSourceFactory<Key, Value> = () -> PagedSource<Key, Value>
 
 /**
  * Base class for an abstraction of pageable static data from some source, where loading pages data
@@ -122,10 +136,12 @@
         /**
          * Optional count of items before the loaded data.
          */
+        @IntRange(from = COUNT_UNDEFINED.toLong())
         val itemsBefore: Int = COUNT_UNDEFINED,
         /**
          * Optional count of items after the loaded data.
          */
+        @IntRange(from = COUNT_UNDEFINED.toLong())
         val itemsAfter: Int = COUNT_UNDEFINED,
         /**
          * Key for next page - ignored unless you're using [KeyProvider.PageKey]
@@ -146,18 +162,15 @@
          *
          * TODO: Investigate refactoring this out of the API now that tiling has been removed.
          */
-        val offset: Int,
-        /**
-         * `true` if the result is an initial load that is passed to
-         * [DataSource.BaseResult.totalCount]. This is a temporary placeholder shadowing
-         * [DataSource.BaseResult.counted] which simply forwards the params to backing
-         * implementations of [PagedSource].
-         */
-        val counted: Boolean = itemsBefore != COUNT_UNDEFINED && itemsAfter != COUNT_UNDEFINED
+        val offset: Int
     ) {
-        internal companion object {
+        internal val counted = itemsBefore != COUNT_UNDEFINED && itemsAfter != COUNT_UNDEFINED
+
+        companion object {
+            const val COUNT_UNDEFINED = -1
+
             @Suppress("MemberVisibilityCanBePrivate") // Prevent synthetic accessor generation.
-            internal val EMPTY = LoadResult(0, 0, null, null, emptyList(), 0, true)
+            internal val EMPTY = LoadResult(0, 0, null, null, emptyList(), 0)
 
             @Suppress("UNCHECKED_CAST") // Can safely ignore, since the list is empty.
             internal fun <Key : Any, Value : Any> empty() = EMPTY as LoadResult<Key, Value>
@@ -182,19 +195,53 @@
 
     abstract val keyProvider: KeyProvider<Key, Value>
 
+    private val onInvalidatedCallbacks = CopyOnWriteArrayList<() -> Unit>()
+
+    private val _invalid = AtomicBoolean(false)
     /**
      * Whether this [PagedSource] has been invalidated, which should happen when the data this
      * [PagedSource] represents changes since it was first instantiated.
      */
-    abstract val invalid: Boolean
+    val invalid: Boolean
+        get() = _invalid.get()
 
     /**
-     * Signal the [PagedSource] to stop loading, and notify its callback.
+     * Signal the [PagedSource] to stop loading.
      *
-     * This method should be idempotent. i.e., If [invalidate] has already been called, subsequent
-     * calls to this method should have no effect.
+     * This method is idempotent. i.e., If [invalidate] has already been called, subsequent calls to
+     * this method should have no effect.
+     *
+     * TODO(b/137971356): Investigate making this not open when able to remove [PagedSourceWrapper].
      */
-    abstract fun invalidate()
+    open fun invalidate() {
+        if (_invalid.compareAndSet(false, true)) {
+            onInvalidatedCallbacks.forEach { it.invoke() }
+        }
+    }
+
+    /**
+     * Add a callback to invoke when the [PagedSource] is first invalidated.
+     *
+     * Once invalidated, a [PagedSource] will not become valid again.
+     *
+     * A [PagedSource] will only invoke its callbacks once - the first time [invalidate] is called,
+     * on that thread.
+     *
+     * @param onInvalidatedCallback The callback that will be invoked on thread that invalidates the
+     * [PagedSource].
+     */
+    fun registerInvalidatedCallback(onInvalidatedCallback: () -> Unit) {
+        onInvalidatedCallbacks.add(onInvalidatedCallback)
+    }
+
+    /**
+     * Remove a previously added invalidate callback.
+     *
+     * @param onInvalidatedCallback The previously added callback.
+     */
+    fun unregisterInvalidatedCallback(onInvalidatedCallback: () -> Unit) {
+        onInvalidatedCallbacks.remove(onInvalidatedCallback)
+    }
 
     /**
      * Loading API for [PagedSource].
@@ -210,9 +257,4 @@
      * @return `false` if the observed error should never be retried, `true` otherwise.
      */
     abstract fun isRetryableError(error: Throwable): Boolean
-
-    companion object {
-        // TODO: Remove this by making itemsBefore and itemsAfter nullable before releasing 3.0.0
-        const val COUNT_UNDEFINED = -1
-    }
 }
diff --git a/paging/common/src/main/kotlin/androidx/paging/PagedSourceWrapper.kt b/paging/common/src/main/kotlin/androidx/paging/PagedSourceWrapper.kt
index d18dd40..38c7a8d 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PagedSourceWrapper.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PagedSourceWrapper.kt
@@ -17,9 +17,6 @@
 package androidx.paging
 
 import androidx.annotation.RestrictTo
-import androidx.paging.PagedSource.Companion.COUNT_UNDEFINED
-import kotlinx.coroutines.asCoroutineDispatcher
-import kotlinx.coroutines.withContext
 
 /**
  * TODO: Move all call-sites dependent on this to use [PagedSource] directly.
@@ -32,6 +29,10 @@
 class PagedSourceWrapper<Key : Any, Value : Any>(
     internal val dataSource: DataSource<Key, Value>
 ) : PagedSource<Key, Value>() {
+    init {
+        dataSource.addInvalidatedCallback { invalidate() }
+    }
+
     override val keyProvider: KeyProvider<Key, Value> = when (dataSource.type) {
         DataSource.KeyType.POSITIONAL -> {
             @Suppress("UNCHECKED_CAST") // Guaranteed to be the correct key type.
@@ -43,11 +44,6 @@
         }
     }
 
-    override val invalid: Boolean
-        get() = dataSource.isInvalid
-
-    override fun invalidate() = dataSource.invalidate()
-
     override suspend fun load(params: LoadParams<Key>): LoadResult<Key, Value> {
         val loadType = when (params.loadType) {
             LoadType.INITIAL -> DataSource.LoadType.INITIAL
@@ -67,72 +63,9 @@
     }
 
     override fun isRetryableError(error: Throwable) = dataSource.isRetryableError(error)
-}
-
-/**
- * TODO: This should no longer be necessary once internal implementation has been moved to used
- * [PagedSource] directly.
- *
- * A wrapper around [PagedSource] which adapts it to the [DataSource] API.
- */
-internal class DataSourceWrapper<Key : Any, Value : Any>(
-    internal val pagedSource: PagedSource<Key, Value>
-) : DataSource<Key, Value>(
-    when (pagedSource.keyProvider) {
-        is PagedSource.KeyProvider.Positional -> KeyType.POSITIONAL
-        is PagedSource.KeyProvider.PageKey -> KeyType.PAGE_KEYED
-        is PagedSource.KeyProvider.ItemKey -> KeyType.ITEM_KEYED
-    }
-) {
-    override suspend fun load(params: Params<Key>): BaseResult<Value> {
-        val loadType = when (params.type) {
-            LoadType.INITIAL -> PagedSource.LoadType.INITIAL
-            LoadType.START -> PagedSource.LoadType.START
-            LoadType.END -> PagedSource.LoadType.END
-        }
-
-        val dataSourceParams = PagedSource.LoadParams(
-            loadType,
-            params.key,
-            params.initialLoadSize,
-            params.placeholdersEnabled,
-            params.pageSize
-        )
-
-        val initialResult = withContext(executor.asCoroutineDispatcher()) {
-            pagedSource.load(dataSourceParams)
-        }
-
-        return BaseResult(
-            initialResult.data,
-            initialResult.prevKey,
-            initialResult.nextKey,
-            if (initialResult.itemsBefore != COUNT_UNDEFINED) initialResult.itemsBefore else 0,
-            if (initialResult.itemsAfter != COUNT_UNDEFINED) initialResult.itemsAfter else 0,
-            initialResult.offset,
-            initialResult.counted
-        )
-    }
-
-    /**
-     * @throws IllegalStateException
-     */
-    override fun getKeyInternal(item: Value): Key {
-        return when (val keyProvider = pagedSource.keyProvider) {
-            is PagedSource.KeyProvider.Positional ->
-                throw IllegalStateException("Cannot get key by item in positionalDataSource")
-            is PagedSource.KeyProvider.PageKey ->
-                throw IllegalStateException("Cannot get key by item in pageKeyedDataSource")
-            is PagedSource.KeyProvider.ItemKey -> keyProvider.getKey(item)
-        }
-    }
-
-    override fun isRetryableError(error: Throwable): Boolean = pagedSource.isRetryableError(error)
-
-    override val isInvalid: Boolean
-        get() = pagedSource.invalid
 
     override fun invalidate() {
-        pagedSource.invalidate()
+        super.invalidate()
+        dataSource.invalidate()
     }
-}
\ No newline at end of file
+}
diff --git a/paging/common/src/main/kotlin/androidx/paging/Pager.kt b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
index 557a333..ca811d1 100644
--- a/paging/common/src/main/kotlin/androidx/paging/Pager.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/Pager.kt
@@ -18,7 +18,7 @@
 
 import androidx.paging.PagedList.LoadState
 import androidx.paging.PagedList.LoadType
-import androidx.paging.PagedSource.Companion.COUNT_UNDEFINED
+import androidx.paging.PagedSource.LoadResult.Companion.COUNT_UNDEFINED
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.asCoroutineDispatcher
 import kotlinx.coroutines.launch
@@ -267,8 +267,8 @@
                 firstLoadedItem = result.data[0]
                 lastLoadedItem = result.data.last()
 
-                if (result.counted) {
-                    counted = true
+                counted = result.counted
+                if (counted) {
                     leadingUnloadedCount = result.itemsBefore
                     trailingUnloadedCount = result.itemsAfter
                 }
diff --git a/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
index 6365c7c..64b9fb4 100644
--- a/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/PositionalDataSource.kt
@@ -24,7 +24,6 @@
 import androidx.paging.PositionalDataSource.LoadInitialCallback
 import kotlinx.coroutines.suspendCancellableCoroutine
 import kotlin.coroutines.resume
-import kotlin.coroutines.resumeWithException
 
 /**
  * Position-based data loader for a fixed-size, countable data set, supporting fixed-size loads at
@@ -182,20 +181,6 @@
          * before the items in data that can be provided by this [DataSource], pass N.
          */
         abstract fun onResult(data: List<T>, position: Int)
-
-        /**
-         * Called to report an error from a DataSource.
-         *
-         * Call this method to report an error from [loadInitial].
-         *
-         * @param error The error that occurred during loading.
-         */
-        open fun onError(error: Throwable) {
-            // TODO: remove default implementation in 3.0
-            throw IllegalStateException(
-                "You must implement onError if implementing your own load callback"
-            )
-        }
     }
 
     /**
@@ -217,20 +202,6 @@
          * unless at end of list.
          */
         abstract fun onResult(data: List<T>)
-
-        /**
-         * Called to report an error from a [DataSource].
-         *
-         * Call this method to report an error from [loadRange].
-         *
-         * @param error The error that occurred during loading.
-         */
-        open fun onError(error: Throwable) {
-            // TODO: remove default implementation in 3.0
-            throw IllegalStateException(
-                "You must implement onError if implementing your own load callback"
-            )
-        }
     }
 
     /**
@@ -424,10 +395,6 @@
                     }
                 }
 
-                override fun onError(error: Throwable) {
-                    cont.resumeWithException(error)
-                }
-
                 private fun resume(params: LoadInitialParams, result: InitialResult<T>) {
                     if (params.placeholdersEnabled) {
                         result.validateForInitialTiling(params.pageSize)
@@ -459,10 +426,6 @@
                         else -> cont.resume(RangeResult(data))
                     }
                 }
-
-                override fun onError(error: Throwable) {
-                    cont.resumeWithException(error)
-                }
             })
         }
 
diff --git a/paging/common/src/main/kotlin/androidx/paging/SnapshotPagedList.kt b/paging/common/src/main/kotlin/androidx/paging/SnapshotPagedList.kt
index 3a74fda..5688543 100644
--- a/paging/common/src/main/kotlin/androidx/paging/SnapshotPagedList.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/SnapshotPagedList.kt
@@ -28,9 +28,6 @@
         lastLoad = pagedList.lastLoad
     }
 
-    override val isContiguous
-        get() = pagedList.isContiguous
-
     override val isImmutable = true
 
     override val lastKey
diff --git a/paging/common/src/main/kotlin/androidx/paging/WrapperItemKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/WrapperItemKeyedDataSource.kt
index 65047b9..386a370 100644
--- a/paging/common/src/main/kotlin/androidx/paging/WrapperItemKeyedDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/WrapperItemKeyedDataSource.kt
@@ -54,7 +54,7 @@
     }
 
     override fun loadInitial(params: LoadInitialParams<K>, callback: LoadInitialCallback<B>) {
-        source.loadInitial(params, object : ItemKeyedDataSource.LoadInitialCallback<A>() {
+        source.loadInitial(params, object : LoadInitialCallback<A>() {
             override fun onResult(data: List<A>, position: Int, totalCount: Int) {
                 callback.onResult(convertWithStashedKeys(data), position, totalCount)
             }
@@ -62,34 +62,22 @@
             override fun onResult(data: List<A>) {
                 callback.onResult(convertWithStashedKeys(data))
             }
-
-            override fun onError(error: Throwable) {
-                callback.onError(error)
-            }
         })
     }
 
     override fun loadAfter(params: LoadParams<K>, callback: LoadCallback<B>) {
-        source.loadAfter(params, object : ItemKeyedDataSource.LoadCallback<A>() {
+        source.loadAfter(params, object : LoadCallback<A>() {
             override fun onResult(data: List<A>) {
                 callback.onResult(convertWithStashedKeys(data))
             }
-
-            override fun onError(error: Throwable) {
-                callback.onError(error)
-            }
         })
     }
 
     override fun loadBefore(params: LoadParams<K>, callback: LoadCallback<B>) {
-        source.loadBefore(params, object : ItemKeyedDataSource.LoadCallback<A>() {
+        source.loadBefore(params, object : LoadCallback<A>() {
             override fun onResult(data: List<A>) {
                 callback.onResult(convertWithStashedKeys(data))
             }
-
-            override fun onError(error: Throwable) {
-                callback.onError(error)
-            }
         })
     }
 
diff --git a/paging/common/src/main/kotlin/androidx/paging/WrapperPageKeyedDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/WrapperPageKeyedDataSource.kt
index c49b480..2f17e3a 100644
--- a/paging/common/src/main/kotlin/androidx/paging/WrapperPageKeyedDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/WrapperPageKeyedDataSource.kt
@@ -34,7 +34,7 @@
     override fun invalidate() = source.invalidate()
 
     override fun loadInitial(params: LoadInitialParams<K>, callback: LoadInitialCallback<K, B>) {
-        source.loadInitial(params, object : PageKeyedDataSource.LoadInitialCallback<K, A>() {
+        source.loadInitial(params, object : LoadInitialCallback<K, A>() {
             override fun onResult(
                 data: List<A>,
                 position: Int,
@@ -50,26 +50,20 @@
                 val convertedData = convert(listFunction, data)
                 callback.onResult(convertedData, previousPageKey, nextPageKey)
             }
-
-            override fun onError(error: Throwable) = callback.onError(error)
         })
     }
 
     override fun loadBefore(params: LoadParams<K>, callback: LoadCallback<K, B>) {
-        source.loadBefore(params, object : PageKeyedDataSource.LoadCallback<K, A>() {
+        source.loadBefore(params, object : LoadCallback<K, A>() {
             override fun onResult(data: List<A>, adjacentPageKey: K?) =
                 callback.onResult(convert(listFunction, data), adjacentPageKey)
-
-            override fun onError(error: Throwable) = callback.onError(error)
         })
     }
 
     override fun loadAfter(params: LoadParams<K>, callback: LoadCallback<K, B>) {
-        source.loadAfter(params, object : PageKeyedDataSource.LoadCallback<K, A>() {
+        source.loadAfter(params, object : LoadCallback<K, A>() {
             override fun onResult(data: List<A>, adjacentPageKey: K?) =
                 callback.onResult(convert(listFunction, data), adjacentPageKey)
-
-            override fun onError(error: Throwable) = callback.onError(error)
         })
     }
 }
diff --git a/paging/common/src/main/kotlin/androidx/paging/WrapperPositionalDataSource.kt b/paging/common/src/main/kotlin/androidx/paging/WrapperPositionalDataSource.kt
index 683d670..8308e87 100644
--- a/paging/common/src/main/kotlin/androidx/paging/WrapperPositionalDataSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/WrapperPositionalDataSource.kt
@@ -40,16 +40,12 @@
 
             override fun onResult(data: List<A>, position: Int) =
                 callback.onResult(convert(listFunction, data), position)
-
-            override fun onError(error: Throwable) = callback.onError(error)
         })
     }
 
     override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<B>) {
         source.loadRange(params, object : LoadRangeCallback<A>() {
             override fun onResult(data: List<A>) = callback.onResult(convert(listFunction, data))
-
-            override fun onError(error: Throwable) = callback.onError(error)
         })
     }
 }
diff --git a/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt b/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
index d5cae09..c548b73 100644
--- a/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
@@ -56,19 +56,10 @@
      */
     private inner class TestPagedSource(val listData: List<Item> = ITEMS) :
         PagedSource<Int, Item>() {
-        private var _invalid = false
-
         override val keyProvider = object : KeyProvider.ItemKey<Int, Item>() {
             override fun getKey(item: Item) = item.pos
         }
 
-        override val invalid: Boolean
-            get() = _invalid
-
-        override fun invalidate() {
-            _invalid = true
-        }
-
         override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Item> {
             return when (params.loadType) {
                 LoadType.INITIAL -> loadInitial(params)
@@ -96,23 +87,22 @@
                     itemsBefore = start,
                     itemsAfter = listData.size - result.size - start,
                     data = result,
-                    offset = start,
-                    counted = false
+                    offset = start
                 )
-                else -> LoadResult(data = result, offset = 0, counted = false)
+                else -> LoadResult(data = result, offset = 0)
             }
         }
 
         private fun loadAfter(params: LoadParams<Int>): LoadResult<Int, Item> {
             val result = getClampedRange(params.key!! + 1, params.key!! + 1 + params.loadSize)
                 ?: throw Exception()
-            return LoadResult(data = result, offset = 0, counted = false)
+            return LoadResult(data = result, offset = 0)
         }
 
         private fun loadBefore(params: LoadParams<Int>): LoadResult<Int, Item> {
             val result =
                 getClampedRange(params.key!! - params.loadSize, params.key!!) ?: throw Exception()
-            return LoadResult(data = result, offset = 0, counted = false)
+            return LoadResult(data = result, offset = 0)
         }
 
         private fun getClampedRange(startInc: Int, endExc: Int): List<Item>? {
diff --git a/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
index b847ea6..4068366 100644
--- a/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
@@ -88,7 +88,6 @@
     fun loadInitial_nullKey() = runBlocking {
         val dataSource = ItemDataSource()
 
-        // dispatchLoadInitial(null, count) == dispatchLoadInitial(count)
         val result = loadInitial(dataSource, null, 10, true)
 
         assertEquals(0, result.leadingNulls)
@@ -212,18 +211,10 @@
         private val counted: Boolean = true,
         private val items: List<Item> = ITEMS_BY_NAME_ID
     ) : ItemKeyedDataSource<Key, Item>() {
-        private var error = false
-
         override fun loadInitial(
             params: LoadInitialParams<Key>,
             callback: LoadInitialCallback<Item>
         ) {
-            if (error) {
-                callback.onError(EXCEPTION)
-                error = false
-                return
-            }
-
             val key = params.requestedInitialKey ?: Key("", Int.MAX_VALUE)
             val start = maxOf(0, findFirstIndexAfter(key) - params.requestedLoadSize / 2)
             val endExclusive = minOf(start + params.requestedLoadSize, items.size)
@@ -236,12 +227,6 @@
         }
 
         override fun loadAfter(params: LoadParams<Key>, callback: LoadCallback<Item>) {
-            if (error) {
-                callback.onError(EXCEPTION)
-                error = false
-                return
-            }
-
             val start = findFirstIndexAfter(params.key)
             val endExclusive = minOf(start + params.requestedLoadSize, items.size)
 
@@ -249,12 +234,6 @@
         }
 
         override fun loadBefore(params: LoadParams<Key>, callback: LoadCallback<Item>) {
-            if (error) {
-                callback.onError(EXCEPTION)
-                error = false
-                return
-            }
-
             val firstIndexBefore = findFirstIndexBefore(params.key)
             val endExclusive = maxOf(0, firstIndexBefore + 1)
             val start = maxOf(0, firstIndexBefore - params.requestedLoadSize + 1)
@@ -277,10 +256,6 @@
                 KEY_COMPARATOR.compare(key, getKey(items[it])) > 0
             } ?: -1
         }
-
-        fun enqueueError() {
-            error = true
-        }
     }
 
     private fun performLoadInitial(
@@ -408,10 +383,6 @@
                 override fun onResult(data: List<A>) {
                     callback.onResult(convert(data))
                 }
-
-                override fun onError(error: Throwable) {
-                    callback.onError(error)
-                }
             })
         }
 
@@ -420,10 +391,6 @@
                 override fun onResult(data: List<A>) {
                     callback.onResult(convert(data))
                 }
-
-                override fun onError(error: Throwable) {
-                    callback.onError(error)
-                }
             })
         }
 
@@ -432,10 +399,6 @@
                 override fun onResult(data: List<A>) {
                     callback.onResult(convert(data))
                 }
-
-                override fun onError(error: Throwable) {
-                    callback.onError(error)
-                }
             })
         }
 
@@ -473,12 +436,8 @@
             loadInitialCallback
         )
         verify(loadInitialCallback).onResult(
-            ITEMS_BY_NAME_ID.subList(0, 10).map { DecoratedItem(it) })
-        //     error
-        orig.enqueueError()
-        wrapper.loadInitial(initParams, loadInitialCallback)
-        verify(loadInitialCallback).onError(EXCEPTION)
-        verifyNoMoreInteractions(loadInitialCallback)
+            ITEMS_BY_NAME_ID.subList(0, 10).map { DecoratedItem(it) }
+        )
 
         val key = orig.getKey(ITEMS_BY_NAME_ID[20])
         @Suppress("UNCHECKED_CAST")
@@ -487,22 +446,12 @@
         // load after
         wrapper.loadAfter(ItemKeyedDataSource.LoadParams(key, 10), loadCallback)
         verify(loadCallback).onResult(ITEMS_BY_NAME_ID.subList(21, 31).map { DecoratedItem(it) })
-        // load after - error
-        orig.enqueueError()
-        wrapper.loadAfter(ItemKeyedDataSource.LoadParams(key, 10), loadCallback)
-        verify(loadCallback).onError(EXCEPTION)
-        verifyNoMoreInteractions(loadCallback)
 
         // load before
         @Suppress("UNCHECKED_CAST")
         loadCallback = mock()
         wrapper.loadBefore(ItemKeyedDataSource.LoadParams(key, 10), loadCallback)
         verify(loadCallback).onResult(ITEMS_BY_NAME_ID.subList(10, 20).map { DecoratedItem(it) })
-        // load before - error
-        orig.enqueueError()
-        wrapper.loadBefore(ItemKeyedDataSource.LoadParams(key, 10), loadCallback)
-        verify(loadCallback).onError(EXCEPTION)
-        verifyNoMoreInteractions(loadCallback)
 
         // verify invalidation
         orig.invalidate()
@@ -555,7 +504,5 @@
                 (Math.random() * 200).toInt().toString() + " fake st."
             )
         }.sortedWith(ITEM_COMPARATOR)
-
-        private val EXCEPTION = Exception()
     }
 }
diff --git a/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
index fc82d9a..af58970 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
@@ -57,7 +57,6 @@
             callback: LoadInitialCallback<String, Item>
         ) {
             if (error) {
-                callback.onError(EXCEPTION)
                 error = false
                 return
             }
@@ -68,7 +67,6 @@
 
         override fun loadBefore(params: LoadParams<String>, callback: LoadCallback<String, Item>) {
             if (error) {
-                callback.onError(EXCEPTION)
                 error = false
                 return
             }
@@ -79,7 +77,6 @@
 
         override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<String, Item>) {
             if (error) {
-                callback.onError(EXCEPTION)
                 error = false
                 return
             }
@@ -269,7 +266,7 @@
         val pagedListJob = testCoroutineScope.async(executor.asCoroutineDispatcher()) {
             PagedList.create(
                 PagedSourceWrapper(dataSource),
-                GlobalScope,
+                testCoroutineScope,
                 executor,
                 executor,
                 executor,
@@ -399,10 +396,6 @@
                 override fun onResult(data: List<A>, previousPageKey: K?, nextPageKey: K?) {
                     callback.onResult(convert(data), previousPageKey, nextPageKey)
                 }
-
-                override fun onError(error: Throwable) {
-                    callback.onError(error)
-                }
             })
         }
 
@@ -411,10 +404,6 @@
                 override fun onResult(data: List<A>, adjacentPageKey: K?) {
                     callback.onResult(convert(data), adjacentPageKey)
                 }
-
-                override fun onError(error: Throwable) {
-                    callback.onError(error)
-                }
             })
         }
 
@@ -423,10 +412,6 @@
                 override fun onResult(data: List<A>, adjacentPageKey: K?) {
                     callback.onResult(convert(data), adjacentPageKey)
                 }
-
-                override fun onError(error: Throwable) {
-                    callback.onError(error)
-                }
             })
         }
 
@@ -471,7 +456,6 @@
         // load after - error
         orig.enqueueError()
         wrapper.loadAfter(PageKeyedDataSource.LoadParams(expectedInitial.next, 4), loadCallback)
-        verify(loadCallback).onError(EXCEPTION)
         verifyNoMoreInteractions(loadCallback)
 
         // load before
@@ -487,7 +471,6 @@
         // load before - error
         orig.enqueueError()
         wrapper.loadBefore(PageKeyedDataSource.LoadParams(expectedAfter.prev, 4), loadCallback)
-        verify(loadCallback).onError(EXCEPTION)
         verifyNoMoreInteractions(loadCallback)
 
         // verify invalidation
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
index 28b1658..4cad7b8 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagedListTest.kt
@@ -23,7 +23,6 @@
 import kotlinx.coroutines.async
 import kotlinx.coroutines.runBlocking
 import org.junit.Assert.assertEquals
-import org.junit.Assert.fail
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -39,22 +38,13 @@
         private val pagedSource = object : PagedSource<Int, String>() {
             override val keyProvider = KeyProvider.Positional<String>()
 
-            private var _invalid = false
-            override val invalid: Boolean
-                get() = _invalid
-
-            override fun invalidate() {
-                _invalid = true
-            }
-
             override suspend fun load(params: LoadParams<Int>): LoadResult<Int, String> =
                 when (params.loadType) {
                     LoadType.INITIAL -> LoadResult(
                         0,
                         0,
                         data = listOf("a"),
-                        offset = 0,
-                        counted = true
+                        offset = 0
                     )
                     else -> throw NotImplementedError("Test should fail if we get here")
                 }
@@ -110,17 +100,14 @@
 
     @Test
     fun createAsyncThrow() {
-        val dataSource = object : PositionalDataSource<String>() {
-            override fun loadInitial(
-                params: LoadInitialParams,
-                callback: LoadInitialCallback<String>
-            ) {
-                callback.onError(Exception())
+        val pagedSource = object : PagedSource<Int, String>() {
+            override val keyProvider = KeyProvider.Positional<String>()
+
+            override suspend fun load(params: LoadParams<Int>): LoadResult<Int, String> {
+                throw Exception()
             }
 
-            override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<String>) {
-                fail("no load range expected")
-            }
+            override fun isRetryableError(error: Throwable) = false
         }
 
         val config = PagedList.Config.Builder()
@@ -131,7 +118,7 @@
         assertFails {
             val job = testCoroutineScope.async(backgroundThread.asCoroutineDispatcher()) {
                 PagedList.create(
-                    PagedSourceWrapper(dataSource),
+                    pagedSource,
                     testCoroutineScope,
                     mainThread,
                     backgroundThread,
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt
new file mode 100644
index 0000000..3ef86fb
--- /dev/null
+++ b/paging/common/src/test/kotlin/androidx/paging/PagedSourceTest.kt
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.paging
+
+import androidx.paging.PagedSource.LoadParams
+import androidx.paging.PagedSource.LoadResult
+import androidx.paging.PagedSource.LoadResult.Companion.COUNT_UNDEFINED
+import androidx.paging.PagedSource.LoadType
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertFailsWith
+
+@RunWith(JUnit4::class)
+class PagedSourceTest {
+
+    // ----- STANDARD -----
+
+    private suspend fun loadInitial(
+        pagedSource: ItemDataSource,
+        key: Key?,
+        initialLoadSize: Int,
+        enablePlaceholders: Boolean
+    ): LoadResult<Key, Item> {
+        return pagedSource.load(
+            LoadParams(
+                LoadType.INITIAL,
+                key,
+                initialLoadSize,
+                enablePlaceholders,
+                10
+            )
+        )
+    }
+
+    @Test
+    fun loadInitial() {
+        runBlocking {
+            val pagedSource = ItemDataSource()
+            val key = pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
+            val result = loadInitial(pagedSource, key, 10, true)
+
+            assertEquals(45, result.itemsBefore)
+            assertEquals(ITEMS_BY_NAME_ID.subList(45, 55), result.data)
+            assertEquals(45, result.itemsAfter)
+
+            // Verify error is propagated correctly.
+            pagedSource.enqueueError()
+            val errorParams = LoadParams(LoadType.INITIAL, key, 10, false, 10)
+            assertFailsWith<CustomException> {
+                pagedSource.load(errorParams)
+            }
+        }
+    }
+
+    @Test
+    fun loadInitial_keyMatchesSingleItem() = runBlocking {
+        val pagedSource = ItemDataSource(items = ITEMS_BY_NAME_ID.subList(0, 1))
+
+        // this is tricky, since load after and load before with the passed key will fail
+        val result =
+            loadInitial(pagedSource, pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID[0]), 20, true)
+
+        assertEquals(0, result.itemsBefore)
+        assertEquals(ITEMS_BY_NAME_ID.subList(0, 1), result.data)
+        assertEquals(0, result.itemsAfter)
+    }
+
+    @Test
+    fun loadInitial_keyMatchesLastItem() = runBlocking {
+        val pagedSource = ItemDataSource()
+
+        // tricky, because load after key is empty, so another load before and load after required
+        val key = pagedSource.keyProvider.getKey(ITEMS_BY_NAME_ID.last())
+        val result = loadInitial(pagedSource, key, 20, true)
+
+        assertEquals(90, result.itemsBefore)
+        assertEquals(ITEMS_BY_NAME_ID.subList(90, 100), result.data)
+        assertEquals(0, result.itemsAfter)
+    }
+
+    @Test
+    fun loadInitial_nullKey() = runBlocking {
+        val dataSource = ItemDataSource()
+
+        val result = loadInitial(dataSource, null, 10, true)
+
+        assertEquals(0, result.itemsBefore)
+        assertEquals(ITEMS_BY_NAME_ID.subList(0, 10), result.data)
+        assertEquals(90, result.itemsAfter)
+    }
+
+    @Test
+    fun loadInitial_keyPastEndOfList() = runBlocking {
+        val dataSource = ItemDataSource()
+
+        // if key is past entire data set, should return last items in data set
+        val key = Key("fz", 0)
+        val result = loadInitial(dataSource, key, 10, true)
+
+        // NOTE: ideally we'd load 10 items here, but it adds complexity and unpredictability to
+        // do: load after was empty, so pass full size to load before, since this can incur larger
+        // loads than requested (see keyMatchesLastItem test)
+        assertEquals(95, result.itemsBefore)
+        assertEquals(ITEMS_BY_NAME_ID.subList(95, 100), result.data)
+        assertEquals(0, result.itemsAfter)
+    }
+
+    // ----- UNCOUNTED -----
+
+    @Test
+    fun loadInitial_disablePlaceholders() = runBlocking {
+        val dataSource = ItemDataSource()
+
+        // dispatchLoadInitial(key, count) == null padding, loadAfter(key, count), null padding
+        val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
+        val result = loadInitial(dataSource, key, 10, false)
+
+        assertEquals(COUNT_UNDEFINED, result.itemsBefore)
+        assertEquals(ITEMS_BY_NAME_ID.subList(45, 55), result.data)
+        assertEquals(COUNT_UNDEFINED, result.itemsAfter)
+    }
+
+    @Test
+    fun loadInitial_uncounted() = runBlocking {
+        val dataSource = ItemDataSource(counted = false)
+
+        // dispatchLoadInitial(key, count) == null padding, loadAfter(key, count), null padding
+        val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
+        val result = loadInitial(dataSource, key, 10, true)
+
+        assertEquals(COUNT_UNDEFINED, result.itemsBefore)
+        assertEquals(ITEMS_BY_NAME_ID.subList(45, 55), result.data)
+        assertEquals(COUNT_UNDEFINED, result.itemsAfter)
+    }
+
+    @Test
+    fun loadInitial_nullKey_uncounted() = runBlocking {
+        val dataSource = ItemDataSource(counted = false)
+
+        val result = loadInitial(dataSource, null, 10, true)
+
+        assertEquals(COUNT_UNDEFINED, result.itemsBefore)
+        assertEquals(ITEMS_BY_NAME_ID.subList(0, 10), result.data)
+        assertEquals(COUNT_UNDEFINED, result.itemsAfter)
+    }
+
+    // ----- EMPTY -----
+
+    @Test
+    fun loadInitial_empty() = runBlocking {
+        val dataSource = ItemDataSource(items = ArrayList())
+
+        // dispatchLoadInitial(key, count) == null padding, loadAfter(key, count), null padding
+        val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[49])
+        val result = loadInitial(dataSource, key, 10, true)
+
+        assertEquals(0, result.itemsBefore)
+        assertTrue(result.data.isEmpty())
+        assertEquals(0, result.itemsAfter)
+    }
+
+    @Test
+    fun loadInitial_nullKey_empty() = runBlocking {
+        val dataSource = ItemDataSource(items = ArrayList())
+        val result = loadInitial(dataSource, null, 10, true)
+
+        assertEquals(0, result.itemsBefore)
+        assertTrue(result.data.isEmpty())
+        assertEquals(0, result.itemsAfter)
+    }
+
+    // ----- Other behavior -----
+
+    @Test
+    fun loadBefore() {
+        val dataSource = ItemDataSource()
+
+        runBlocking {
+            val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[5])
+            val params = LoadParams(LoadType.START, key, 5, false, 5)
+            val observed = dataSource.load(params).data
+
+            assertEquals(ITEMS_BY_NAME_ID.subList(0, 5), observed)
+
+            // Verify error is propagated correctly.
+            dataSource.enqueueError()
+            assertFailsWith<CustomException> {
+                val errorParams = LoadParams(LoadType.START, key, 5, false, 5)
+                dataSource.load(errorParams)
+            }
+        }
+    }
+
+    @Test
+    fun loadAfter() {
+        val dataSource = ItemDataSource()
+
+        runBlocking {
+            val key = dataSource.keyProvider.getKey(ITEMS_BY_NAME_ID[5])
+            val params = LoadParams(LoadType.END, key, 5, false, 5)
+            val observed = dataSource.load(params).data
+
+            assertEquals(ITEMS_BY_NAME_ID.subList(6, 11), observed)
+
+            // Verify error is propagated correctly.
+            dataSource.enqueueError()
+            assertFailsWith<CustomException> {
+                val errorParams = LoadParams(LoadType.END, key, 5, false, 5)
+                dataSource.load(errorParams)
+            }
+        }
+    }
+
+    internal data class Key(val name: String, val id: Int)
+
+    internal data class Item(
+        val name: String,
+        val id: Int,
+        val balance: Double,
+        val address: String
+    )
+
+    internal class ItemDataSource(
+        private val counted: Boolean = true,
+        private val items: List<Item> = ITEMS_BY_NAME_ID
+    ) : PagedSource<Key, Item>() {
+        private var error = false
+
+        override val keyProvider = object : KeyProvider.ItemKey<Key, Item>() {
+            override fun getKey(item: Item) = Key(item.name, item.id)
+        }
+
+        override suspend fun load(params: LoadParams<Key>): LoadResult<Key, Item> {
+            return when (params.loadType) {
+                LoadType.INITIAL -> loadInitial(params)
+                LoadType.START -> loadBefore(params)
+                LoadType.END -> loadAfter(params)
+            }
+        }
+
+        override fun isRetryableError(error: Throwable) = false
+
+        private fun loadInitial(params: LoadParams<Key>): LoadResult<Key, Item> {
+            if (error) {
+                error = false
+                throw EXCEPTION
+            }
+
+            val key = params.key ?: Key("", Int.MAX_VALUE)
+            val start = maxOf(0, findFirstIndexAfter(key) - params.loadSize / 2)
+            val endExclusive = minOf(start + params.loadSize, items.size)
+
+            return if (params.placeholdersEnabled && counted) {
+                val data = items.subList(start, endExclusive)
+                LoadResult(
+                    itemsBefore = start,
+                    itemsAfter = items.size - data.size - start,
+                    data = data,
+                    offset = start
+                )
+            } else {
+                LoadResult(data = items.subList(start, endExclusive), offset = 0)
+            }
+        }
+
+        private fun loadAfter(params: LoadParams<Key>): LoadResult<Key, Item> {
+            if (error) {
+                error = false
+                throw EXCEPTION
+            }
+
+            val start = findFirstIndexAfter(params.key!!)
+            val endExclusive = minOf(start + params.loadSize, items.size)
+
+            return LoadResult(data = items.subList(start, endExclusive), offset = 0)
+        }
+
+        private fun loadBefore(params: LoadParams<Key>): LoadResult<Key, Item> {
+            if (error) {
+                error = false
+                throw EXCEPTION
+            }
+
+            val firstIndexBefore = findFirstIndexBefore(params.key!!)
+            val endExclusive = maxOf(0, firstIndexBefore + 1)
+            val start = maxOf(0, firstIndexBefore - params.loadSize + 1)
+
+            return LoadResult(data = items.subList(start, endExclusive), offset = 0)
+        }
+
+        private fun findFirstIndexAfter(key: Key): Int {
+            return items.indices.firstOrNull {
+                KEY_COMPARATOR.compare(key, keyProvider.getKey(items[it])) < 0
+            } ?: items.size
+        }
+
+        private fun findFirstIndexBefore(key: Key): Int {
+            return items.indices.reversed().firstOrNull {
+                KEY_COMPARATOR.compare(key, keyProvider.getKey(items[it])) > 0
+            } ?: -1
+        }
+
+        fun enqueueError() {
+            error = true
+        }
+    }
+
+    class CustomException : Exception()
+
+    companion object {
+        private val ITEM_COMPARATOR = compareBy<Item> { it.name }.thenByDescending { it.id }
+        private val KEY_COMPARATOR = compareBy<Key> { it.name }.thenByDescending { it.id }
+
+        private val ITEMS_BY_NAME_ID = List(100) {
+            val names = Array(10) { index -> "f" + ('a' + index) }
+            Item(
+                names[it % 10],
+                it,
+                Math.random() * 1000,
+                (Math.random() * 200).toInt().toString() + " fake st."
+            )
+        }.sortedWith(ITEM_COMPARATOR)
+
+        private val EXCEPTION = CustomException()
+    }
+}
diff --git a/paging/common/src/test/kotlin/androidx/paging/PagedSourceWrapperTest.kt b/paging/common/src/test/kotlin/androidx/paging/PagedSourceWrapperTest.kt
index eca3a11..3f8b5f2 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PagedSourceWrapperTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PagedSourceWrapperTest.kt
@@ -20,6 +20,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
 
 @RunWith(JUnit4::class)
 class PagedSourceWrapperTest {
@@ -44,10 +46,15 @@
             override fun getKey(item: String) = item.hashCode()
         }
         val pagedSource = PagedSourceWrapper(dataSource)
-        assert(pagedSource.keyProvider is PagedSource.KeyProvider.ItemKey)
-        assert(!pagedSource.invalid)
+        assertTrue { pagedSource.keyProvider is PagedSource.KeyProvider.ItemKey }
+
+        assertFalse { pagedSource.invalid }
+        assertFalse { dataSource.isInvalid }
+
         pagedSource.invalidate()
-        assert(pagedSource.invalid)
+
+        assertTrue { pagedSource.invalid }
+        assertTrue { dataSource.isInvalid }
     }
 
     @Test
@@ -69,30 +76,62 @@
             }
         }
         val pagedSource = PagedSourceWrapper(dataSource)
-        assert(pagedSource.keyProvider is PagedSource.KeyProvider.PageKey)
-        assert(!pagedSource.invalid)
+        assertTrue { pagedSource.keyProvider is PagedSource.KeyProvider.PageKey }
+
+        assertFalse { pagedSource.invalid }
+        assertFalse { dataSource.isInvalid }
+
         pagedSource.invalidate()
-        assert(pagedSource.invalid)
+
+        assertTrue { pagedSource.invalid }
+        assertTrue { dataSource.isInvalid }
     }
 
     @Test
     fun positional() {
-        val dataSource = object : PositionalDataSource<String>() {
-            override fun loadInitial(
-                params: LoadInitialParams,
-                callback: LoadInitialCallback<String>
-            ) {
-                Assert.fail("loadInitial not expected")
-            }
-
-            override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<String>) {
-                Assert.fail("loadRange not expected")
-            }
-        }
+        val dataSource = createTestPositionalDataSource()
         val pagedSource = PagedSourceWrapper(dataSource)
-        assert(pagedSource.keyProvider is PagedSource.KeyProvider.Positional)
-        assert(!pagedSource.invalid)
+        assertTrue { pagedSource.keyProvider is PagedSource.KeyProvider.Positional }
+    }
+
+    @Test
+    fun invalidateFromPagedSource() {
+        val dataSource = createTestPositionalDataSource()
+        val pagedSource = PagedSourceWrapper(dataSource)
+
+        assertFalse { pagedSource.invalid }
+        assertFalse { dataSource.isInvalid }
+
         pagedSource.invalidate()
-        assert(pagedSource.invalid)
+
+        assertTrue { pagedSource.invalid }
+        assertTrue { dataSource.isInvalid }
+    }
+
+    @Test
+    fun invalidateFromDataSource() {
+        val dataSource = createTestPositionalDataSource()
+        val pagedSource = PagedSourceWrapper(dataSource)
+
+        assertFalse { pagedSource.invalid }
+        assertFalse { dataSource.isInvalid }
+
+        dataSource.invalidate()
+
+        assertTrue { pagedSource.invalid }
+        assertTrue { dataSource.isInvalid }
+    }
+
+    private fun createTestPositionalDataSource() = object : PositionalDataSource<String>() {
+        override fun loadInitial(
+            params: LoadInitialParams,
+            callback: LoadInitialCallback<String>
+        ) {
+            Assert.fail("loadInitial not expected")
+        }
+
+        override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<String>) {
+            Assert.fail("loadRange not expected")
+        }
     }
 }
diff --git a/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
index 5c449fe..475fc4f 100644
--- a/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/PositionalDataSourceTest.kt
@@ -287,10 +287,6 @@
                 override fun onResult(data: List<A>, position: Int) {
                     callback.onResult(convert(data), position)
                 }
-
-                override fun onError(error: Throwable) {
-                    callback.onError(error)
-                }
             })
         }
 
@@ -299,10 +295,6 @@
                 override fun onResult(data: List<A>) {
                     callback.onResult(convert(data))
                 }
-
-                override fun onError(error: Throwable) {
-                    callback.onError(error)
-                }
             })
         }
 
@@ -321,7 +313,6 @@
 
         override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<T>) {
             if (error) {
-                callback.onError(ERROR)
                 error = false
                 return
             }
@@ -338,7 +329,6 @@
 
         override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<T>) {
             if (error) {
-                callback.onError(ERROR)
                 error = false
                 return
             }
@@ -368,7 +358,6 @@
         // load initial - error
         orig.enqueueError()
         wrapper.loadInitial(initParams, loadInitialCallback)
-        verify(loadInitialCallback).onError(ERROR)
         verifyNoMoreInteractions(loadInitialCallback)
 
         // load range
@@ -380,7 +369,6 @@
         // load range - error
         orig.enqueueError()
         wrapper.loadRange(PositionalDataSource.LoadRangeParams(2, 3), loadRangeCallback)
-        verify(loadRangeCallback).onError(ERROR)
         verifyNoMoreInteractions(loadRangeCallback)
 
         // check invalidation behavior
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
index cc831a34..44b20ff 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/ItemDataSource.kt
@@ -18,22 +18,30 @@
 
 import android.graphics.Color
 import androidx.annotation.ColorInt
-import androidx.paging.PositionalDataSource
+import androidx.paging.PagedSource
 import java.util.ArrayList
 import java.util.concurrent.atomic.AtomicBoolean
 
 val dataSourceError = AtomicBoolean(false)
+
 /**
  * Sample data source with artificial data.
  */
-internal class ItemDataSource : PositionalDataSource<Item>() {
+internal class ItemDataSource : PagedSource<Int, Item>() {
+    override val keyProvider = KeyProvider.Positional<Item>()
+
+    override suspend fun load(params: LoadParams<Int>) = when (params.loadType) {
+        LoadType.INITIAL -> loadInitial(params)
+        else -> loadRange(params)
+    }
+
     class RetryableItemError : Exception()
 
     private val mGenerationId = sGenerationId++
 
-    private fun loadRangeInternal(startPosition: Int, loadCount: Int): List<Item>? {
+    private fun loadRangeInternal(startPosition: Int, loadCount: Int): List<Item> {
         val items = ArrayList<Item>()
-        val end = Math.min(COUNT, startPosition + loadCount)
+        val end = minOf(COUNT, startPosition + loadCount)
         val bgColor = COLORS[mGenerationId % COLORS.size]
 
         Thread.sleep(1000)
@@ -45,41 +53,68 @@
             items.add(Item(i, "item $i", bgColor))
         }
         if (dataSourceError.compareAndSet(true, false)) {
-            return null
+            throw RetryableItemError()
         }
         return items
     }
 
+    override fun isRetryableError(error: Throwable): Boolean {
+        return error is RetryableItemError
+    }
+
     companion object {
         private const val COUNT = 60
 
         @ColorInt
         private val COLORS = intArrayOf(Color.RED, Color.BLUE, Color.BLACK)
-
         private var sGenerationId: Int = 0
     }
 
-    override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<Item>) {
-        val position = computeInitialLoadPosition(params, COUNT)
-        val loadSize = computeInitialLoadSize(params, position, COUNT)
+    // TODO: Clean up logic only pertinent to tiling.
+    private fun computeStartPosition(params: LoadParams<Int>): Int {
+        val requestedStartPosition = params.key?.let { key ->
+            var initialPosition = key
+
+            if (params.placeholdersEnabled) {
+                // snap load size to page multiple (minimum two)
+                val initialLoadSize = maxOf(params.loadSize / params.pageSize, 2) * params.pageSize
+
+                // move start so the load is centered around the key, not starting at it
+                val idealStart = initialPosition - initialLoadSize / 2
+                initialPosition = maxOf(0, idealStart / params.pageSize * params.pageSize)
+            } else {
+                // not tiled, so don't try to snap or force multiple of a page size
+                initialPosition -= params.loadSize / 2
+            }
+
+            initialPosition
+        } ?: 0
+
+        var pageStart = requestedStartPosition / params.pageSize * params.pageSize
+
+        // maximum start pos is that which will encompass end of list
+        val maximumLoadPage =
+            (COUNT - params.loadSize + params.pageSize - 1) / params.pageSize * params.pageSize
+        pageStart = minOf(maximumLoadPage, pageStart)
+
+        // minimum start position is 0
+        return maxOf(0, pageStart)
+    }
+
+    private fun loadInitial(params: LoadParams<Int>): LoadResult<Int, Item> {
+        val position = computeStartPosition(params)
+        val loadSize = minOf(COUNT - position, params.loadSize)
         val data = loadRangeInternal(position, loadSize)
-        if (data == null) {
-            callback.onError(RetryableItemError())
-        } else {
-            callback.onResult(data, position, COUNT)
-        }
+        return LoadResult(
+            itemsBefore = position,
+            itemsAfter = COUNT - data.size - position,
+            data = data,
+            offset = 0
+        )
     }
 
-    override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<Item>) {
-        val data = loadRangeInternal(params.startPosition, params.loadSize)
-        if (data == null) {
-            callback.onError(RetryableItemError())
-        } else {
-            callback.onResult(data)
-        }
-    }
-
-    override fun isRetryableError(error: Throwable): Boolean {
-        return error is RetryableItemError
+    private fun loadRange(params: LoadParams<Int>): LoadResult<Int, Item> {
+        val data = loadRangeInternal(params.key ?: 0, params.loadSize)
+        return LoadResult(data = data, offset = 0)
     }
 }
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/PagedListItemViewModel.java b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/PagedListItemViewModel.java
index 5f2c5f3..31263ea 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/PagedListItemViewModel.java
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/custom/PagedListItemViewModel.java
@@ -16,12 +16,16 @@
 
 package androidx.paging.integration.testapp.custom;
 
+import android.annotation.SuppressLint;
+
 import androidx.annotation.NonNull;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.ViewModel;
-import androidx.paging.DataSource;
 import androidx.paging.LivePagedListBuilder;
 import androidx.paging.PagedList;
+import androidx.paging.PagedSource;
+
+import kotlin.jvm.functions.Function0;
 
 /**
  * Sample ViewModel backed by an artificial data source
@@ -30,22 +34,22 @@
     private ItemDataSource mDataSource;
     private final Object mDataSourceLock = new Object();
 
-    private final DataSource.Factory<Integer, Item> mFactory =
-            new DataSource.Factory<Integer, Item>() {
-        @NonNull
-        @Override
-        public DataSource<Integer, Item> create() {
-            ItemDataSource newDataSource = new ItemDataSource();
-            synchronized (mDataSourceLock) {
-                mDataSource = newDataSource;
-                return mDataSource;
-            }
-        }
-    };
+    private final Function0<PagedSource<Integer, Item>> mFactory =
+            new Function0<PagedSource<Integer, Item>>() {
+                @SuppressLint("SyntheticAccessor")
+                @NonNull
+                @Override
+                public PagedSource<Integer, Item> invoke() {
+                    ItemDataSource newDataSource = new ItemDataSource();
+                    synchronized (mDataSourceLock) {
+                        mDataSource = newDataSource;
+                        return mDataSource;
+                    }
+                }
+            };
 
     private LiveData<PagedList<Item>> mLivePagedList =
-            new LivePagedListBuilder<>(mFactory, 10)
-                    .build();
+            new LivePagedListBuilder<>(mFactory, 10).build();
 
     void invalidateList() {
         synchronized (mDataSourceLock) {
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListActivity.java b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListActivity.java
index 9f3d041..501ec82 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListActivity.java
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListActivity.java
@@ -43,8 +43,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_room_recycler_view);
         // TODO use by viewModels() once this class switches to Kotlin
-        final CustomerViewModel viewModel = new ViewModelProvider(this,
-                ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()))
+        final CustomerViewModel viewModel = new ViewModelProvider(this)
                 .get(CustomerViewModel.class);
 
         mRecyclerView = findViewById(R.id.recyclerview);
diff --git a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListRxActivity.java b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListRxActivity.java
index 54fe3bf..359c9b9 100644
--- a/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListRxActivity.java
+++ b/paging/integration-tests/testapp/src/main/java/androidx/paging/integration/testapp/room/RoomPagedListRxActivity.java
@@ -41,8 +41,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_recycler_view);
         // TODO use by viewModels() once this class switches to Kotlin
-        mViewModel = new ViewModelProvider(this,
-                ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()))
+        mViewModel = new ViewModelProvider(this)
                 .get(CustomerViewModel.class);
 
         RecyclerView recyclerView = findViewById(R.id.recyclerview);
diff --git a/paging/runtime/api/3.0.0-alpha01.txt b/paging/runtime/api/3.0.0-alpha01.txt
index b5b45fe..f035ad8 100644
--- a/paging/runtime/api/3.0.0-alpha01.txt
+++ b/paging/runtime/api/3.0.0-alpha01.txt
@@ -28,8 +28,10 @@
   }
 
   public final class LivePagedListBuilder<Key, Value> {
-    ctor public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
-    ctor public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, int pageSize);
+    ctor @Deprecated public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
+    ctor @Deprecated public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, int pageSize);
+    ctor public LivePagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public LivePagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, int pageSize);
     method public androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> build();
     method public androidx.paging.LivePagedListBuilder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
     method public androidx.paging.LivePagedListBuilder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
@@ -39,8 +41,10 @@
 
   public final class LivePagedListKt {
     ctor public LivePagedListKt();
-    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, androidx.paging.PagedList.Config config, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
-    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method @Deprecated public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, androidx.paging.PagedList.Config config, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method @Deprecated public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>>, androidx.paging.PagedList.Config config, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
   }
 
   public abstract class PagedListAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
diff --git a/paging/runtime/api/current.txt b/paging/runtime/api/current.txt
index b5b45fe..f035ad8 100644
--- a/paging/runtime/api/current.txt
+++ b/paging/runtime/api/current.txt
@@ -28,8 +28,10 @@
   }
 
   public final class LivePagedListBuilder<Key, Value> {
-    ctor public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
-    ctor public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, int pageSize);
+    ctor @Deprecated public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
+    ctor @Deprecated public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, int pageSize);
+    ctor public LivePagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public LivePagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, int pageSize);
     method public androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> build();
     method public androidx.paging.LivePagedListBuilder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
     method public androidx.paging.LivePagedListBuilder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
@@ -39,8 +41,10 @@
 
   public final class LivePagedListKt {
     ctor public LivePagedListKt();
-    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, androidx.paging.PagedList.Config config, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
-    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method @Deprecated public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, androidx.paging.PagedList.Config config, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method @Deprecated public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>>, androidx.paging.PagedList.Config config, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
   }
 
   public abstract class PagedListAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
diff --git a/paging/runtime/api/restricted_3.0.0-alpha01.txt b/paging/runtime/api/restricted_3.0.0-alpha01.txt
index b5b45fe..f035ad8 100644
--- a/paging/runtime/api/restricted_3.0.0-alpha01.txt
+++ b/paging/runtime/api/restricted_3.0.0-alpha01.txt
@@ -28,8 +28,10 @@
   }
 
   public final class LivePagedListBuilder<Key, Value> {
-    ctor public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
-    ctor public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, int pageSize);
+    ctor @Deprecated public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
+    ctor @Deprecated public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, int pageSize);
+    ctor public LivePagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public LivePagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, int pageSize);
     method public androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> build();
     method public androidx.paging.LivePagedListBuilder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
     method public androidx.paging.LivePagedListBuilder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
@@ -39,8 +41,10 @@
 
   public final class LivePagedListKt {
     ctor public LivePagedListKt();
-    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, androidx.paging.PagedList.Config config, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
-    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method @Deprecated public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, androidx.paging.PagedList.Config config, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method @Deprecated public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>>, androidx.paging.PagedList.Config config, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
   }
 
   public abstract class PagedListAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
diff --git a/paging/runtime/api/restricted_current.txt b/paging/runtime/api/restricted_current.txt
index b5b45fe..f035ad8 100644
--- a/paging/runtime/api/restricted_current.txt
+++ b/paging/runtime/api/restricted_current.txt
@@ -28,8 +28,10 @@
   }
 
   public final class LivePagedListBuilder<Key, Value> {
-    ctor public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
-    ctor public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, int pageSize);
+    ctor @Deprecated public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, androidx.paging.PagedList.Config config);
+    ctor @Deprecated public LivePagedListBuilder(androidx.paging.DataSource.Factory<Key,Value> dataSourceFactory, int pageSize);
+    ctor public LivePagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, androidx.paging.PagedList.Config config);
+    ctor public LivePagedListBuilder(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>> pagedSourceFactory, int pageSize);
     method public androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> build();
     method public androidx.paging.LivePagedListBuilder<Key,Value> setBoundaryCallback(androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback);
     method public androidx.paging.LivePagedListBuilder<Key,Value> setCoroutineScope(kotlinx.coroutines.CoroutineScope coroutineScope);
@@ -39,8 +41,10 @@
 
   public final class LivePagedListKt {
     ctor public LivePagedListKt();
-    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, androidx.paging.PagedList.Config config, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
-    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method @Deprecated public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, androidx.paging.PagedList.Config config, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method @Deprecated public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(androidx.paging.DataSource.Factory<Key,Value>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>>, androidx.paging.PagedList.Config config, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
+    method public static <Key, Value> androidx.lifecycle.LiveData<androidx.paging.PagedList<Value>> toLiveData(kotlin.jvm.functions.Function0<? extends androidx.paging.PagedSource<Key,Value>>, int pageSize, Key? initialLoadKey = null, androidx.paging.PagedList.BoundaryCallback<Value>? boundaryCallback = null, java.util.concurrent.Executor fetchExecutor = ArchTaskExecutor.getIOThreadExecutor());
   }
 
   public abstract class PagedListAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
index c60ab6f..d10028a 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
@@ -80,9 +80,9 @@
         ArchTaskExecutor.getInstance().setDelegate(null)
     }
 
-    class MockDataSourceFactory : DataSource.Factory<Int, String>() {
-        override fun create(): DataSource<Int, String> {
-            return MockDataSource()
+    class MockDataSourceFactory {
+        fun create(): PagedSource<Int, String> {
+            return MockPagedSource()
         }
 
         var throwable: Throwable? = null
@@ -91,28 +91,35 @@
             throwable = RETRYABLE_EXCEPTION
         }
 
-        private inner class MockDataSource : PositionalDataSource<String>() {
-            override fun loadInitial(
-                params: LoadInitialParams,
-                callback: LoadInitialCallback<String>
-            ) {
+        private inner class MockPagedSource : PagedSource<Int, String>() {
+            override val keyProvider = KeyProvider.Positional<String>()
+
+            override suspend fun load(params: LoadParams<Int>) = when (params.loadType) {
+                LoadType.INITIAL -> loadInitial(params)
+                else -> loadRange()
+            }
+
+            override fun isRetryableError(error: Throwable) = error === RETRYABLE_EXCEPTION
+
+            private fun loadInitial(params: LoadParams<Int>): LoadResult<Int, String> {
                 assertEquals(2, params.pageSize)
 
-                if (throwable != null) {
-
-                    callback.onError(throwable!!)
+                throwable?.let { error ->
                     throwable = null
-                } else {
-                    callback.onResult(listOf("a", "b"), 0, 4)
+                    throw error
                 }
+
+                val data = listOf("a", "b")
+                return LoadResult(
+                    itemsBefore = 0,
+                    itemsAfter = 4 - data.size,
+                    data = data,
+                    offset = 0
+                )
             }
 
-            override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<String>) {
-                callback.onResult(listOf("c", "d"))
-            }
-
-            override fun isRetryableError(error: Throwable): Boolean {
-                return error === RETRYABLE_EXCEPTION
+            private fun loadRange(): LoadResult<Int, String> {
+                return LoadResult(data = listOf("c", "d"), offset = 0)
             }
         }
     }
@@ -121,7 +128,7 @@
     fun executorBehavior() {
         // specify a background executor via builder, and verify it gets used for all loads,
         // overriding default arch IO executor
-        val livePagedList = LivePagedListBuilder(MockDataSourceFactory(), 2)
+        val livePagedList = LivePagedListBuilder(MockDataSourceFactory()::create, 2)
             .setFetchExecutor(backgroundExecutor)
             .build()
 
@@ -160,7 +167,7 @@
         val factory = MockDataSourceFactory()
         factory.enqueueRetryableError()
 
-        val livePagedList = LivePagedListBuilder(factory, 2)
+        val livePagedList = LivePagedListBuilder(factory::create, 2)
             .setFetchExecutor(backgroundExecutor)
             .build()
 
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListTest.kt
index 1d48562..995d1a8 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListTest.kt
@@ -33,7 +33,8 @@
     val instantTaskExecutorRule = InstantTaskExecutorRule()
 
     @Test
-    fun toLiveData_config() {
+    fun toLiveData_dataSourceConfig() {
+        @Suppress("DEPRECATION")
         val livePagedList = dataSourceFactory.toLiveData(config)
         livePagedList.observeForever {}
         assertNotNull(livePagedList.value)
@@ -41,13 +42,31 @@
     }
 
     @Test
-    fun toLiveData_pageSize() {
+    fun toLiveData_dataSourcePageSize() {
+        @Suppress("DEPRECATION")
         val livePagedList = dataSourceFactory.toLiveData(24)
         livePagedList.observeForever {}
         assertNotNull(livePagedList.value)
         assertEquals(24, livePagedList.value!!.config.pageSize)
     }
 
+    @Test
+    fun toLiveData_pagedSourceConfig() {
+        @Suppress("DEPRECATION")
+        val livePagedList = pagedSourceFactory.toLiveData(config)
+        livePagedList.observeForever {}
+        assertNotNull(livePagedList.value)
+        assertEquals(config, livePagedList.value!!.config)
+    }
+
+    @Test
+    fun toLiveData_pagedSourcePageSize() {
+        val livePagedList = pagedSourceFactory.toLiveData(24)
+        livePagedList.observeForever {}
+        assertNotNull(livePagedList.value)
+        assertEquals(24, livePagedList.value!!.config.pageSize)
+    }
+
     companion object {
         private val dataSource = object : PositionalDataSource<String>() {
             override fun loadInitial(
@@ -66,6 +85,10 @@
             }
         }
 
+        private val pagedSource = PagedSourceWrapper(dataSource)
+
+        private val pagedSourceFactory = { pagedSource }
+
         private val config = Config(10)
     }
 }
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt b/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt
index e626f99..20c42d4 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/StringPagedList.kt
@@ -44,8 +44,6 @@
         )
     }
 
-    override val isContiguous = true
-
     override val lastKey: Any? = null
 
     override val isDetached
diff --git a/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.kt b/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.kt
index 9f737ba..39754e2 100644
--- a/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.kt
+++ b/paging/runtime/src/main/java/androidx/paging/AsyncPagedListDiffer.kt
@@ -68,7 +68,7 @@
  *     @Override
  *     public void onCreate(Bundle savedState) {
  *         super.onCreate(savedState);
- *         MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
+ *         MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
  *         RecyclerView recyclerView = findViewById(R.id.user_list);
  *         final UserAdapter adapter = new UserAdapter();
  *         viewModel.usersList.observe(this, pagedList -> adapter.submitList(pagedList));
@@ -131,7 +131,6 @@
 
     @VisibleForTesting
     internal val listeners = CopyOnWriteArrayList<PagedListListener<T>>()
-    private var isContiguous: Boolean = false
     private var pagedList: PagedList<T>? = null
     private var snapshot: PagedList<T>? = null
 
@@ -282,16 +281,6 @@
      *                       it is committed.
      */
     open fun submitList(pagedList: PagedList<T>?, commitCallback: Runnable?) {
-        if (pagedList != null) {
-            if (currentList == null) {
-                isContiguous = pagedList.isContiguous
-            } else if (pagedList.isContiguous != isContiguous) {
-                throw IllegalArgumentException(
-                    "AsyncPagedListDiffer cannot handle both contiguous and non-contiguous lists."
-                )
-            }
-        }
-
         // incrementing generation means any currently-running diffs are discarded when they finish
         val runGeneration = ++maxScheduledGeneration
 
diff --git a/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt b/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt
index 95da82c..ce6fcde6d 100644
--- a/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt
+++ b/paging/runtime/src/main/java/androidx/paging/LivePagedList.kt
@@ -30,7 +30,7 @@
     initialKey: Key?,
     private val config: PagedList.Config,
     private val boundaryCallback: PagedList.BoundaryCallback<Value>?,
-    private val dataSourceFactory: DataSource.Factory<Key, Value>,
+    private val pagedSourceFactory: PagedSourceFactory<Key, Value>,
     private val notifyExecutor: Executor,
     private val fetchExecutor: Executor
 ) : LiveData<PagedList<Value>>() {
@@ -43,7 +43,7 @@
 
     init {
         currentData = InitialPagedList(
-            PagedSourceWrapper(dataSourceFactory.create()),
+            pagedSourceFactory(),
             coroutineScope,
             config,
             initialKey
@@ -97,16 +97,15 @@
     }
 
     private suspend fun createPagedList(): PagedList<Value> {
-        val dataSource = dataSourceFactory.create()
-        @Suppress("DEPRECATION")
-        currentData.dataSource.removeInvalidatedCallback(callback)
-        dataSource.addInvalidatedCallback(callback)
+        val pagedSource = pagedSourceFactory()
+        currentData.pagedSource.unregisterInvalidatedCallback(callback)
+        pagedSource.registerInvalidatedCallback(callback)
         currentData.setInitialLoadState(PagedList.LoadState.LOADING, null)
 
         @Suppress("UNCHECKED_CAST") // getLastKey guaranteed to be of 'Key' type
         val lastKey = currentData.lastKey as Key?
         return PagedList.create(
-            PagedSourceWrapper(dataSource),
+            pagedSource,
             coroutineScope,
             notifyExecutor,
             fetchExecutor,
@@ -132,12 +131,14 @@
  *
  * @see LivePagedListBuilder
  */
+@Deprecated("DataSource is deprecated and has been replaced by PagedSource")
 fun <Key : Any, Value : Any> DataSource.Factory<Key, Value>.toLiveData(
     config: PagedList.Config,
     initialLoadKey: Key? = null,
     boundaryCallback: PagedList.BoundaryCallback<Value>? = null,
     fetchExecutor: Executor = ArchTaskExecutor.getIOThreadExecutor()
 ): LiveData<PagedList<Value>> {
+    @Suppress("DEPRECATION")
     return LivePagedListBuilder(this, config)
         .setInitialLoadKey(initialLoadKey)
         .setBoundaryCallback(boundaryCallback)
@@ -159,12 +160,68 @@
  *
  * @see LivePagedListBuilder
  */
+@Deprecated("DataSource is deprecated and has been replaced by PagedSource")
 fun <Key : Any, Value : Any> DataSource.Factory<Key, Value>.toLiveData(
     pageSize: Int,
     initialLoadKey: Key? = null,
     boundaryCallback: PagedList.BoundaryCallback<Value>? = null,
     fetchExecutor: Executor = ArchTaskExecutor.getIOThreadExecutor()
 ): LiveData<PagedList<Value>> {
+    @Suppress("DEPRECATION")
+    return LivePagedListBuilder(this, Config(pageSize))
+        .setInitialLoadKey(initialLoadKey)
+        .setBoundaryCallback(boundaryCallback)
+        .setFetchExecutor(fetchExecutor)
+        .build()
+}
+
+/**
+ * Constructs a `LiveData<PagedList>`, from this [PagedSourceFactory], convenience for
+ * [LivePagedListBuilder].
+ *
+ * No work (such as loading) is done immediately, the creation of the first PagedList is is
+ * deferred until the LiveData is observed.
+ *
+ * @param config Paging configuration.
+ * @param initialLoadKey Initial load key passed to the first PagedList/PagedSource.
+ * @param boundaryCallback The boundary callback for listening to PagedList load state.
+ * @param fetchExecutor Executor for fetching data from PagedSources.
+ *
+ * @see LivePagedListBuilder
+ */
+fun <Key : Any, Value : Any> PagedSourceFactory<Key, Value>.toLiveData(
+    config: PagedList.Config,
+    initialLoadKey: Key? = null,
+    boundaryCallback: PagedList.BoundaryCallback<Value>? = null,
+    fetchExecutor: Executor = ArchTaskExecutor.getIOThreadExecutor()
+): LiveData<PagedList<Value>> {
+    return LivePagedListBuilder(this, config)
+        .setInitialLoadKey(initialLoadKey)
+        .setBoundaryCallback(boundaryCallback)
+        .setFetchExecutor(fetchExecutor)
+        .build()
+}
+
+/**
+ * Constructs a `LiveData<PagedList>`, from this [PagedSourceFactory], convenience for
+ * [LivePagedListBuilder].
+ *
+ * No work (such as loading) is done immediately, the creation of the first PagedList is is
+ * deferred until the LiveData is observed.
+ *
+ * @param pageSize Page size.
+ * @param initialLoadKey Initial load key passed to the first PagedList/PagedSource.
+ * @param boundaryCallback The boundary callback for listening to PagedList load state.
+ * @param fetchExecutor Executor for fetching data from PagedSources.
+ *
+ * @see LivePagedListBuilder
+ */
+fun <Key : Any, Value : Any> PagedSourceFactory<Key, Value>.toLiveData(
+    pageSize: Int,
+    initialLoadKey: Key? = null,
+    boundaryCallback: PagedList.BoundaryCallback<Value>? = null,
+    fetchExecutor: Executor = ArchTaskExecutor.getIOThreadExecutor()
+): LiveData<PagedList<Value>> {
     return LivePagedListBuilder(this, Config(pageSize))
         .setInitialLoadKey(initialLoadKey)
         .setBoundaryCallback(boundaryCallback)
diff --git a/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.kt b/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.kt
index afd1183..ae1c147 100644
--- a/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.kt
+++ b/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.kt
@@ -29,25 +29,32 @@
  * The required parameters are in the constructor, so you can simply construct and build, or
  * optionally enable extra features (such as initial load key, or BoundaryCallback).
  *
- * @param Key Type of input valued used to load data from the DataSource. Must be integer if you're
- *            using PositionalDataSource.
+ * @param Key Type of input valued used to load data from the [DataSource]. Must be integer if
+ * you're using [PositionalDataSource].
  * @param Value Item type being presented.
- *
- * @constructor Creates a LivePagedListBuilder with required parameters.
- * @param dataSourceFactory DataSource factory providing DataSource generations.
- * @param config Paging configuration.
  */
-class LivePagedListBuilder<Key : Any, Value : Any>(
-    private val dataSourceFactory: DataSource.Factory<Key, Value>,
+class LivePagedListBuilder<Key : Any, Value : Any> {
+    private val pagedSourceFactory: PagedSourceFactory<Key, Value>
     private val config: PagedList.Config
-) {
     private var coroutineScope: CoroutineScope = GlobalScope
     private var initialLoadKey: Key? = null
     private var boundaryCallback: PagedList.BoundaryCallback<Value>? = null
     private var fetchExecutor = ArchTaskExecutor.getIOThreadExecutor()
 
     /**
-     * Creates a LivePagedListBuilder with required parameters.
+     * Creates a [LivePagedListBuilder] with required parameters.
+     *
+     * @param dataSourceFactory [DataSource] factory providing DataSource generations.
+     * @param config Paging configuration.
+     */
+    @Deprecated("DataSource is deprecated and has been replaced by PagedSource")
+    constructor(dataSourceFactory: DataSource.Factory<Key, Value>, config: PagedList.Config) {
+        this.pagedSourceFactory = { PagedSourceWrapper(dataSourceFactory.create()) }
+        this.config = config
+    }
+
+    /**
+     * Creates a [LivePagedListBuilder] with required parameters.
      *
      * This method is a convenience for:
      * ```
@@ -58,12 +65,58 @@
      * @param dataSourceFactory [DataSource.Factory] providing DataSource generations.
      * @param pageSize Size of pages to load.
      */
+    @Suppress("DEPRECATION")
+    @Deprecated("DataSource is deprecated and has been replaced by PagedSource")
     constructor(dataSourceFactory: DataSource.Factory<Key, Value>, pageSize: Int) : this(
         dataSourceFactory,
         PagedList.Config.Builder().setPageSize(pageSize).build()
     )
 
     /**
+     * Creates a [LivePagedListBuilder] with required parameters.
+     *
+     * @param pagedSourceFactory [PagedSource] factory providing [PagedSource] generations.
+     *
+     * The returned [PagedSource] should invalidate itself if the snapshot is no longer valid. If a
+     * [PagedSource] becomes invalid, the only way to query more data is to create a new
+     * [PagedSource] by invoking the supplied [pagedSourceFactory].
+     *
+     * [pagedSourceFactory] will invoked to construct a new [PagedList] and [PagedSource] when the
+     * current [PagedSource] is invalidated, and pass the new [PagedList] through the
+     * `LiveData<PagedList>` to observers.
+     * @param config Paging configuration.
+     */
+    constructor(pagedSourceFactory: PagedSourceFactory<Key, Value>, config: PagedList.Config) {
+        this.pagedSourceFactory = pagedSourceFactory
+        this.config = config
+    }
+
+    /**
+     * Creates a [LivePagedListBuilder] with required parameters.
+     *
+     * This method is a convenience for:
+     * ```
+     * LivePagedListBuilder(pagedSourceFactory,
+     *         new PagedList.Config.Builder().setPageSize(pageSize).build())
+     * ```
+     *
+     * @param pagedSourceFactory [PagedSource] factory providing [PagedSource] generations.
+     *
+     * The returned [PagedSource] should invalidate itself if the snapshot is no longer valid. If a
+     * [PagedSource] becomes invalid, the only way to query more data is to create a new
+     * [PagedSource] by invoking the supplied [pagedSourceFactory].
+     *
+     * [pagedSourceFactory] will invoked to construct a new [PagedList] and [PagedSource] when the
+     * current [PagedSource] is invalidated, and pass the new [PagedList] through the
+     * `LiveData<PagedList>` to observers.
+     * @param pageSize Size of pages to load.
+     */
+    constructor(pagedSourceFactory: PagedSourceFactory<Key, Value>, pageSize: Int) : this(
+        pagedSourceFactory,
+        PagedList.Config.Builder().setPageSize(pageSize).build()
+    )
+
+    /**
      * Set the [CoroutineScope] that page loads should be launched within. The set [coroutineScope]
      * allows a [PagedSource] to cancel running load operations when the results are no longer
      * needed - for example, when the containing activity is destroyed.
@@ -141,7 +194,7 @@
             initialLoadKey,
             config,
             boundaryCallback,
-            dataSourceFactory,
+            pagedSourceFactory,
             ArchTaskExecutor.getMainThreadExecutor(),
             fetchExecutor
         )
diff --git a/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.kt b/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.kt
index e3849ad..a40db41 100644
--- a/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.kt
+++ b/paging/runtime/src/main/java/androidx/paging/PagedListAdapter.kt
@@ -59,7 +59,7 @@
  *     @Override
  *     public void onCreate(Bundle savedState) {
  *         super.onCreate(savedState);
- *         MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
+ *         MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
  *         RecyclerView recyclerView = findViewById(R.id.user_list);
  *         UserAdapter&lt;User> adapter = new UserAdapter();
  *         viewModel.usersList.observe(this, pagedList -> adapter.submitList(pagedList));
diff --git a/paging/rxjava2/src/test/java/androidx/paging/RxPagedListBuilderTest.kt b/paging/rxjava2/src/test/java/androidx/paging/RxPagedListBuilderTest.kt
index 3cb666c..40152ff 100644
--- a/paging/rxjava2/src/test/java/androidx/paging/RxPagedListBuilderTest.kt
+++ b/paging/rxjava2/src/test/java/androidx/paging/RxPagedListBuilderTest.kt
@@ -23,6 +23,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
+import kotlin.test.assertTrue
 
 @RunWith(JUnit4::class)
 class RxPagedListBuilderTest {
@@ -63,9 +64,11 @@
         assertEquals(listOf("a", "b"), observer.values().first())
 
         // invalidate triggers second load
-        observer.values().first().pagedSource.invalidate()
+        @Suppress("DEPRECATION")
+        observer.values().first().dataSource.invalidate()
         scheduler.triggerActions()
         observer.assertValueCount(2)
+        assertTrue { observer.values().first().pagedSource.invalid }
         assertEquals(listOf("c", "d"), observer.values().last())
     }
 
diff --git a/palette/palette/build.gradle b/palette/palette/build.gradle
index 1a785db..87eb258b 100644
--- a/palette/palette/build.gradle
+++ b/palette/palette/build.gradle
@@ -9,7 +9,7 @@
 }
 
 dependencies {
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.1.0")
 
     annotationProcessor(NULLAWAY)
diff --git a/percentlayout/percentlayout/build.gradle b/percentlayout/percentlayout/build.gradle
index 5b2ac05..e5c8470 100644
--- a/percentlayout/percentlayout/build.gradle
+++ b/percentlayout/percentlayout/build.gradle
@@ -10,7 +10,7 @@
 
 dependencies {
     api(ANDROIDX_ANNOTATION)
-    implementation("androidx.core:core:1.1.0-rc01")
+    implementation("androidx.core:core:1.1.0")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/preference/build.gradle b/preference/build.gradle
index 43b0f07..8235c7a 100644
--- a/preference/build.gradle
+++ b/preference/build.gradle
@@ -30,7 +30,7 @@
     implementation("androidx.annotation:annotation:1.1.0")
     api("androidx.appcompat:appcompat:1.1.0-rc01")
     // TODO: change to alpha05 after release
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.fragment:fragment:1.1.0-rc01")
     api("androidx.recyclerview:recyclerview:1.0.0")
diff --git a/recyclerview/recyclerview-benchmark/build.gradle b/recyclerview/recyclerview-benchmark/build.gradle
index 87a0edb..8b18e14 100644
--- a/recyclerview/recyclerview-benchmark/build.gradle
+++ b/recyclerview/recyclerview-benchmark/build.gradle
@@ -19,12 +19,13 @@
     id("AndroidXPlugin")
     id("com.android.library")
     id("kotlin-android")
+    id("androidx.benchmark")
 }
 
 dependencies {
     androidTestImplementation(project(":appcompat"))
     androidTestImplementation(project(":recyclerview:recyclerview"))
-    androidTestImplementation(project(":benchmark"))
+    androidTestImplementation(project(":benchmark:benchmark-junit4"))
     androidTestImplementation(JUNIT)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/recyclerview/recyclerview-benchmark/src/androidTest/java/androidx/recyclerview/benchmark/ScrollBenchmark.kt b/recyclerview/recyclerview-benchmark/src/androidTest/java/androidx/recyclerview/benchmark/ScrollBenchmark.kt
index 2dec083..8acf597 100644
--- a/recyclerview/recyclerview-benchmark/src/androidTest/java/androidx/recyclerview/benchmark/ScrollBenchmark.kt
+++ b/recyclerview/recyclerview-benchmark/src/androidTest/java/androidx/recyclerview/benchmark/ScrollBenchmark.kt
@@ -20,8 +20,8 @@
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
-import androidx.benchmark.BenchmarkRule
-import androidx.benchmark.measureRepeated
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
 import androidx.recyclerview.benchmark.test.R
 import androidx.recyclerview.widget.RecyclerView
 import androidx.test.annotation.UiThreadTest
diff --git a/recyclerview/recyclerview-selection/api/restricted_1.0.0.txt b/recyclerview/recyclerview-selection/api/restricted_1.0.0.txt
index 8281ab7..358c695 100644
--- a/recyclerview/recyclerview-selection/api/restricted_1.0.0.txt
+++ b/recyclerview/recyclerview-selection/api/restricted_1.0.0.txt
@@ -22,31 +22,6 @@
     method public boolean canInitiate(android.view.MotionEvent);
   }
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class DefaultSelectionTracker<K> extends androidx.recyclerview.selection.SelectionTracker<K> {
-    ctor public DefaultSelectionTracker(String, androidx.recyclerview.selection.ItemKeyProvider, androidx.recyclerview.selection.SelectionTracker.SelectionPredicate, androidx.recyclerview.selection.StorageStrategy<K!>);
-    method public void addObserver(androidx.recyclerview.selection.SelectionTracker.SelectionObserver);
-    method public void anchorRange(int);
-    method public void clearProvisionalSelection();
-    method public boolean clearSelection();
-    method public void copySelection(androidx.recyclerview.selection.MutableSelection);
-    method public boolean deselect(K);
-    method public void endRange();
-    method public void extendProvisionalRange(int);
-    method public void extendRange(int);
-    method public androidx.recyclerview.selection.Selection! getSelection();
-    method public boolean hasSelection();
-    method public boolean isRangeActive();
-    method public boolean isSelected(K?);
-    method public void mergeProvisionalSelection();
-    method public final void onRestoreInstanceState(android.os.Bundle?);
-    method public final void onSaveInstanceState(android.os.Bundle);
-    method protected void restoreSelection(androidx.recyclerview.selection.Selection);
-    method public boolean select(K);
-    method public boolean setItemsSelected(Iterable<K!>, boolean);
-    method public void setProvisionalSelection(java.util.Set<K!>);
-    method public void startRange(int);
-  }
-
   public abstract class FocusDelegate<K> {
     ctor public FocusDelegate();
     method public abstract void clearFocus();
diff --git a/recyclerview/recyclerview-selection/api/restricted_1.1.0-alpha07.ignore b/recyclerview/recyclerview-selection/api/restricted_1.1.0-alpha07.ignore
index 3f1f595..21be473 100644
--- a/recyclerview/recyclerview-selection/api/restricted_1.1.0-alpha07.ignore
+++ b/recyclerview/recyclerview-selection/api/restricted_1.1.0-alpha07.ignore
@@ -1,25 +1,3 @@
 // Baseline format: 1.0
-AddedAbstractMethod: androidx.recyclerview.selection.SelectionTracker#anchorRange(int):
-    Added method androidx.recyclerview.selection.SelectionTracker.anchorRange(int)
-AddedAbstractMethod: androidx.recyclerview.selection.SelectionTracker#clearProvisionalSelection():
-    Added method androidx.recyclerview.selection.SelectionTracker.clearProvisionalSelection()
-AddedAbstractMethod: androidx.recyclerview.selection.SelectionTracker#endRange():
-    Added method androidx.recyclerview.selection.SelectionTracker.endRange()
-AddedAbstractMethod: androidx.recyclerview.selection.SelectionTracker#extendProvisionalRange(int):
-    Added method androidx.recyclerview.selection.SelectionTracker.extendProvisionalRange(int)
-AddedAbstractMethod: androidx.recyclerview.selection.SelectionTracker#extendRange(int):
-    Added method androidx.recyclerview.selection.SelectionTracker.extendRange(int)
-AddedAbstractMethod: androidx.recyclerview.selection.SelectionTracker#getAdapterDataObserver():
-    Added method androidx.recyclerview.selection.SelectionTracker.getAdapterDataObserver()
-AddedAbstractMethod: androidx.recyclerview.selection.SelectionTracker#isRangeActive():
-    Added method androidx.recyclerview.selection.SelectionTracker.isRangeActive()
-AddedAbstractMethod: androidx.recyclerview.selection.SelectionTracker#mergeProvisionalSelection():
-    Added method androidx.recyclerview.selection.SelectionTracker.mergeProvisionalSelection()
-AddedAbstractMethod: androidx.recyclerview.selection.SelectionTracker#setProvisionalSelection(java.util.Set<K>):
-    Added method androidx.recyclerview.selection.SelectionTracker.setProvisionalSelection(java.util.Set<K>)
-AddedAbstractMethod: androidx.recyclerview.selection.SelectionTracker#startRange(int):
-    Added method androidx.recyclerview.selection.SelectionTracker.startRange(int)
-
-
 RemovedClass: androidx.recyclerview.selection.AutoScroller:
     Removed class androidx.recyclerview.selection.AutoScroller
diff --git a/recyclerview/recyclerview-selection/build.gradle b/recyclerview/recyclerview-selection/build.gradle
index ac2f787..a364be0 100644
--- a/recyclerview/recyclerview-selection/build.gradle
+++ b/recyclerview/recyclerview-selection/build.gradle
@@ -27,7 +27,7 @@
 dependencies {
     api(project(":recyclerview:recyclerview"))
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.1.0")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/recyclerview/recyclerview/build.gradle b/recyclerview/recyclerview/build.gradle
index 16d7734..4ec3faf 100644
--- a/recyclerview/recyclerview/build.gradle
+++ b/recyclerview/recyclerview/build.gradle
@@ -11,7 +11,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0-rc02")
+    api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.customview:customview:1.0.0")
 
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerExtraLayoutSpaceTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerExtraLayoutSpaceTest.java
index 40e4916..70b1225 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerExtraLayoutSpaceTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerExtraLayoutSpaceTest.java
@@ -304,7 +304,7 @@
                 mRecordExtraLayoutSpace = false;
                 mRecordedExtraLayoutSpace[0] = extraLayoutSpace[0];
                 mRecordedExtraLayoutSpace[1] = extraLayoutSpace[1];
-                getViewTreeObserver().addOnDrawListener(mLayoutRecorder);
+                getViewTreeObserver().addOnPreDrawListener(mLayoutRecorder);
             }
         }
 
@@ -316,7 +316,7 @@
         }
     }
 
-    class LayoutBoundsRecorder implements ViewTreeObserver.OnDrawListener {
+    class LayoutBoundsRecorder implements ViewTreeObserver.OnPreDrawListener {
         private final OrientationHelper mHelper;
         private final int[][] mBounds;
 
@@ -331,16 +331,17 @@
         }
 
         @Override
-        public void onDraw() {
+        public boolean onPreDraw() {
             if (!mHasRecorded) {
                 recordBounds();
                 mRecyclerView.post(new Runnable() {
                     @Override
                     public void run() {
-                        getViewTreeObserver().removeOnDrawListener(LayoutBoundsRecorder.this);
+                        getViewTreeObserver().removeOnPreDrawListener(LayoutBoundsRecorder.this);
                     }
                 });
             }
+            return true;
         }
 
         private void recordBounds() {
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAccessibilityLifecycleTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAccessibilityLifecycleTest.java
index 0abcd0f..a5bc429 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAccessibilityLifecycleTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAccessibilityLifecycleTest.java
@@ -161,7 +161,7 @@
     }
 
     @Test
-    public void notClearCustomViewDelegate() throws Throwable {
+    public void notClearCustomViewDelegateAndMaintainItemDelegate() throws Throwable {
         final RecyclerView recyclerView = new RecyclerView(getActivity()) {
             @Override
             boolean isAccessibilityEnabled() {
@@ -203,7 +203,7 @@
         recyclerView.setItemViewCacheSize(0); // no cache, directly goes to pool
         recyclerView.setLayoutManager(layoutManager);
         setRecyclerView(recyclerView);
-         mActivityRule.runOnUiThread(new Runnable() {
+        mActivityRule.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 recyclerView.setAdapter(adapter);
@@ -223,9 +223,9 @@
                     AccessibilityNodeInfo info = recyclerView.getChildAt(i)
                             .createAccessibilityNodeInfo();
                     assertTrue("custom delegate sets isChecked", info.isChecked());
-                    assertFalse(recyclerView.findContainingViewHolder(view).hasAnyOfTheFlags(
-                            RecyclerView.ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE));
-                    assertTrue(delegateCompat.equals(ViewCompat.getAccessibilityDelegate(view)));
+                    if (Build.VERSION.SDK_INT >= 19) {
+                        assertNotNull(info.getCollectionItemInfo());
+                    }
                     children.add(view);
                 }
             }
@@ -248,9 +248,9 @@
                     assertTrue(children.contains(view));
                     AccessibilityNodeInfo info = view.createAccessibilityNodeInfo();
                     assertTrue("custom delegate sets isChecked", info.isChecked());
-                    assertFalse(recyclerView.findContainingViewHolder(view).hasAnyOfTheFlags(
-                            RecyclerView.ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE));
-                    assertTrue(delegateCompat.equals(ViewCompat.getAccessibilityDelegate(view)));
+                    if (Build.VERSION.SDK_INT >= 19) {
+                        assertNotNull(info.getCollectionItemInfo());
+                    }
                 }
             }
         });
@@ -287,6 +287,7 @@
             @Override
             public void run() {
                 recyclerView.setAdapter(adapter);
+
             }
         });
         layoutManager.waitForLayout(1);
@@ -298,8 +299,6 @@
                 for (int i = 0; i < recyclerView.getChildCount(); i++) {
                     View view = recyclerView.getChildAt(i);
                     assertEquals(i, recyclerView.getChildAdapterPosition(view));
-                    assertTrue(recyclerView.findContainingViewHolder(view).hasAnyOfTheFlags(
-                            RecyclerView.ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE));
                     assertTrue(accessibiltyDelegateIsItemDelegate(recyclerView, view));
                     AccessibilityNodeInfo info = view.createAccessibilityNodeInfo();
                     if (Build.VERSION.SDK_INT >= 19) {
@@ -326,8 +325,6 @@
                     View view = vh.itemView;
                     assertEquals(RecyclerView.NO_POSITION,
                             recyclerView.getChildAdapterPosition(view));
-                    assertFalse(vh.hasAnyOfTheFlags(
-                            RecyclerView.ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE));
                     assertFalse(accessibiltyDelegateIsItemDelegate(recyclerView, view));
                 }
             }
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewNestedScrolling3RequestDisallowInterceptTouchTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewNestedScrolling3RequestDisallowInterceptTouchTest.java
new file mode 100644
index 0000000..931599f
--- /dev/null
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewNestedScrolling3RequestDisallowInterceptTouchTest.java
@@ -0,0 +1,291 @@
+/*
+ * 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.recyclerview.widget;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import static androidx.test.espresso.action.GeneralLocation.CENTER;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.core.view.NestedScrollingParent3;
+import androidx.core.view.NestedScrollingParentHelper;
+import androidx.core.view.ViewCompat;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.testutils.SwipeInjector;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Large integration tests that verify that a {@link RecyclerView} interacts with nested scrolling
+ * correctly.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class RecyclerViewNestedScrolling3RequestDisallowInterceptTouchTest {
+
+    private static final int ITEM_HEIGHT = 20;
+    private static final int NSV_HEIGHT = 400;
+    private static final int PARENT_HEIGHT = 400;
+    private static final int WIDTH = 400;
+    private static final int SWIPE_DURATION = 300;
+
+    private RecyclerView mRecyclerView;
+    private NestedScrollingSpyView mParent;
+
+    @Rule
+    public final ActivityTestRule<TestContentViewActivity> mActivityTestRule;
+
+    public RecyclerViewNestedScrolling3RequestDisallowInterceptTouchTest() {
+        mActivityTestRule = new ActivityTestRule<>(TestContentViewActivity.class);
+    }
+
+    @Before
+    public void setUp() throws Throwable {
+        setup();
+        attachToActivity();
+    }
+
+    @Test
+    public void parentConsumes1pxRvConsumes0px() {
+        mParent.consumeX = 0;
+        mParent.consumeY = 1;
+
+        int touchSlop = ViewConfiguration.get(mRecyclerView.getContext()).getScaledTouchSlop();
+        // RecyclerView consumes nothing because we scroll up, and we're already at the top
+        swipeVertically(touchSlop + 100);
+
+        verify(mParent, atLeastOnce()).requestDisallowInterceptTouchEvent(eq(true));
+    }
+
+    @Test
+    public void parentConsumes0pxRvConsumes1px() {
+        mParent.consumeX = 0;
+        mParent.consumeY = 0;
+
+        int touchSlop = ViewConfiguration.get(mRecyclerView.getContext()).getScaledTouchSlop();
+        // RecyclerView consumes all because we scroll down, and we're already at the top
+        swipeVertically(-touchSlop - 1);
+
+        verify(mParent, atLeastOnce()).requestDisallowInterceptTouchEvent(eq(true));
+    }
+
+    @Test
+    public void parentConsumes0pxRvConsumes0px() {
+        mParent.consumeX = 0;
+        mParent.consumeY = 0;
+
+        int touchSlop = ViewConfiguration.get(mRecyclerView.getContext()).getScaledTouchSlop();
+        // RecyclerView consumes nothing because we scroll up, and we're already at the top
+        swipeVertically(touchSlop + 100);
+
+        verify(mParent, never()).requestDisallowInterceptTouchEvent(eq(true));
+    }
+
+    private void setup() {
+        Context context = mActivityTestRule.getActivity();
+
+        mRecyclerView = new RecyclerView(context);
+        mRecyclerView.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, NSV_HEIGHT));
+        mRecyclerView.setBackgroundColor(0xFF0000FF);
+        mRecyclerView.setLayoutManager(new LinearLayoutManager(context));
+        mRecyclerView.setAdapter(new TestAdapter(100, ITEM_HEIGHT));
+
+        mParent = spy(new NestedScrollingSpyView(context));
+        mParent.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, PARENT_HEIGHT));
+        mParent.setBackgroundColor(0xFF0000FF);
+        mParent.addView(mRecyclerView);
+    }
+
+    private void attachToActivity() throws Throwable {
+        final TestContentView testContentView = mActivityTestRule.getActivity().getContentView();
+        testContentView.expectLayouts(1);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                testContentView.addView(mParent);
+            }
+        });
+        testContentView.awaitLayouts(2);
+    }
+
+    private void swipeVertically(int dy) {
+        SwipeInjector swiper = new SwipeInjector(InstrumentationRegistry.getInstrumentation());
+        swiper.startDrag(CENTER, mRecyclerView);
+        swiper.dragBy(0, dy, SWIPE_DURATION);
+        swiper.finishDrag();
+    }
+
+    @SuppressWarnings("NullableProblems")
+    public static class NestedScrollingSpyView extends FrameLayout implements
+            NestedScrollingParent3 {
+
+        private final NestedScrollingParentHelper mNestedScrollingParentHelper;
+        private final int[] mNestedScrollingV2ConsumedCompat = new int[2];
+
+        // The amount of pixels to consume during a nested scroll
+        // Use only positive values, value is used in both directions
+        public int consumeX = 0;
+        public int consumeY = 0;
+
+        public NestedScrollingSpyView(Context context) {
+            super(context);
+            mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
+        }
+
+        // NestedScrollingParent3
+
+        @Override
+        public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
+                int dxUnconsumed, int dyUnconsumed, int type, @NonNull int[] consumed) {
+            if (consumeX != 0) {
+                consumed[0] += dxUnconsumed > 0
+                        ? Math.min(consumeX, dxUnconsumed)
+                        : Math.max(-consumeX, dxUnconsumed);
+            }
+            if (consumeY != 0) {
+                consumed[1] += dyUnconsumed > 0
+                        ? Math.min(consumeY, dyUnconsumed)
+                        : Math.max(-consumeY, dyUnconsumed);
+            }
+        }
+
+        // NestedScrollingParent2
+
+        @Override
+        public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes,
+                int type) {
+            return onStartNestedScroll(child, target, axes);
+        }
+
+        @Override
+        public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes,
+                int type) {
+            onNestedScrollAccepted(child, target, axes);
+        }
+
+        @Override
+        public void onStopNestedScroll(@NonNull View target, int type) {
+            onStopNestedScroll(target);
+        }
+
+        @Override
+        public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
+                int dxUnconsumed, int dyUnconsumed, int type) {
+            onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type,
+                    mNestedScrollingV2ConsumedCompat);
+        }
+
+        @Override
+        public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed,
+                int type) {
+            onNestedPreScroll(target, dx, dy, consumed);
+        }
+
+        // NestedScrollingParent1
+
+        @Override
+        public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
+            return true;
+        }
+
+        @Override
+        public void onNestedScrollAccepted(View child, View target, int axes) {
+            mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes);
+        }
+
+        @Override
+        public void onStopNestedScroll(View child) {
+            mNestedScrollingParentHelper.onStopNestedScroll(child);
+        }
+
+        @Override
+        public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
+                int dyUnconsumed) {
+            onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
+                    ViewCompat.TYPE_TOUCH, mNestedScrollingV2ConsumedCompat);
+        }
+
+        @Override
+        public void onNestedPreScroll(@NonNull View target, int dx, int dy,
+                @NonNull int[] consumed) {
+            ViewCompat.dispatchNestedPreScroll(this, dx, dy, consumed, null);
+        }
+
+        @Override
+        public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY,
+                boolean consumed) {
+            return ViewCompat.dispatchNestedFling(this, velocityX, velocityY, consumed);
+        }
+
+        @Override
+        public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {
+            return ViewCompat.dispatchNestedPreFling(this, velocityX, velocityY);
+        }
+
+        @Override
+        public int getNestedScrollAxes() {
+            return mNestedScrollingParentHelper.getNestedScrollAxes();
+        }
+    }
+
+    private class TestAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+
+        private int mItemHeight;
+        private int mItemCount;
+
+        TestAdapter(int itemCount, int itemHeight) {
+            mItemHeight = itemHeight;
+            mItemCount = itemCount;
+        }
+
+        @NonNull
+        @Override
+        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+            View view = new View(parent.getContext());
+            view.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, mItemHeight));
+            return new RecyclerView.ViewHolder(view) {};
+        }
+
+        @Override
+        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
+        }
+
+        @Override
+        public int getItemCount() {
+            return mItemCount;
+        }
+    }
+
+}
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/AsyncListDiffer.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/AsyncListDiffer.java
index 1c345be03..c1fdb87 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/AsyncListDiffer.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/AsyncListDiffer.java
@@ -67,7 +67,7 @@
  *     {@literal @}Override
  *     public void onCreate(Bundle savedState) {
  *         super.onCreate(savedState);
- *         MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
+ *         MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
  *         RecyclerView recyclerView = findViewById(R.id.user_list);
  *         UserAdapter adapter = new UserAdapter();
  *         viewModel.usersList.observe(this, list -> adapter.submitList(list));
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/ListAdapter.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/ListAdapter.java
index d00e84c..6b1ad73 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/ListAdapter.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/ListAdapter.java
@@ -50,7 +50,7 @@
  *     {@literal @}Override
  *     public void onCreate(Bundle savedState) {
  *         super.onCreate(savedState);
- *         MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
+ *         MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
  *         RecyclerView recyclerView = findViewById(R.id.user_list);
  *         UserAdapter&lt;User> adapter = new UserAdapter();
  *         viewModel.usersList.observe(this, list -> adapter.submitList(list));
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 4dc8c31..74cd10a 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
@@ -1953,6 +1953,7 @@
                 TYPE_TOUCH, mReusableIntPair);
         unconsumedX -= mReusableIntPair[0];
         unconsumedY -= mReusableIntPair[1];
+        boolean consumedNestedScroll = mReusableIntPair[0] != 0 || mReusableIntPair[1] != 0;
 
         // Update the last touch co-ords, taking any scroll offset into account
         mLastTouchX -= mScrollOffset[0];
@@ -1972,7 +1973,7 @@
         if (!awakenScrollBars()) {
             invalidate();
         }
-        return consumedX != 0 || consumedY != 0;
+        return consumedNestedScroll || consumedX != 0 || consumedY != 0;
     }
 
     /**
@@ -6288,14 +6289,16 @@
                     ViewCompat.setImportantForAccessibility(itemView,
                             ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
                 }
-                AccessibilityDelegateCompat delegate =
-                        ViewCompat.getAccessibilityDelegate(itemView);
-                if (delegate == null
-                        || delegate.getClass().equals(AccessibilityDelegateCompat.class)) {
-                    holder.addFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
-                    ViewCompat.setAccessibilityDelegate(itemView,
-                            mAccessibilityDelegate.getItemDelegate());
+                if (mAccessibilityDelegate == null) {
+                    return;
                 }
+                RecyclerViewAccessibilityDelegate.ItemDelegate itemDelegate =
+                        mAccessibilityDelegate.mItemDelegate;
+                // If there was already an a11y delegate set on the itemView, store it in the
+                // itemDelegate and then set the itemDelegate as the a11y delegate.
+                itemDelegate.setOriginalDelegateForItem(itemView,
+                        ViewCompat.getAccessibilityDelegate(itemView));
+                ViewCompat.setAccessibilityDelegate(itemView, itemDelegate);
             }
         }
 
@@ -6504,9 +6507,12 @@
          */
         void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {
             clearNestedRecyclerViewIfNotNested(holder);
-            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE)) {
-                holder.setFlags(0, ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
-                ViewCompat.setAccessibilityDelegate(holder.itemView, null);
+            View itemView = holder.itemView;
+            if (mAccessibilityDelegate != null) {
+                AccessibilityDelegateCompat originalDelegate = mAccessibilityDelegate
+                        .mItemDelegate.getAndRemoveOriginalDelegateForItem(itemView);
+                // Set the a11y delegate back to whatever the original delegate was.
+                ViewCompat.setAccessibilityDelegate(itemView, originalDelegate);
             }
             if (dispatchRecycled) {
                 dispatchViewRecycled(holder);
@@ -11028,12 +11034,6 @@
          */
         static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
 
-        /**
-         * Flags that RecyclerView assigned {@link RecyclerViewAccessibilityDelegate
-         * #getItemDelegate()} in onBindView when app does not provide a delegate.
-         */
-        static final int FLAG_SET_A11Y_ITEM_DELEGATE = 1 << 14;
-
         int mFlags;
 
         private static final List<Object> FULLUPDATE_PAYLOADS = Collections.emptyList();
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerViewAccessibilityDelegate.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerViewAccessibilityDelegate.java
index adfbb43..920929b 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerViewAccessibilityDelegate.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerViewAccessibilityDelegate.java
@@ -24,6 +24,9 @@
 import androidx.core.view.AccessibilityDelegateCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 
+import java.util.Map;
+import java.util.WeakHashMap;
+
 /**
  * The AccessibilityDelegate used by RecyclerView.
  * <p>
@@ -31,7 +34,7 @@
  */
 public class RecyclerViewAccessibilityDelegate extends AccessibilityDelegateCompat {
     final RecyclerView mRecyclerView;
-    final AccessibilityDelegateCompat mItemDelegate;
+    final ItemDelegate mItemDelegate;
 
 
     public RecyclerViewAccessibilityDelegate(@NonNull RecyclerView recyclerView) {
@@ -94,6 +97,7 @@
      */
     public static class ItemDelegate extends AccessibilityDelegateCompat {
         final RecyclerViewAccessibilityDelegate mRecyclerViewDelegate;
+        private Map<View, AccessibilityDelegateCompat> mOriginalItemDelegates = new WeakHashMap<>();
 
         /**
          * Creates an item delegate for the given {@code RecyclerViewAccessibilityDelegate}.
@@ -104,6 +108,17 @@
             mRecyclerViewDelegate = recyclerViewDelegate;
         }
 
+        void setOriginalDelegateForItem(View itemView, AccessibilityDelegateCompat delegate) {
+            if (delegate != null) {
+                mOriginalItemDelegates.put(itemView, delegate);
+            }
+
+        }
+
+        AccessibilityDelegateCompat getAndRemoveOriginalDelegateForItem(View itemView) {
+            return mOriginalItemDelegates.remove(itemView);
+        }
+
         @Override
         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
             super.onInitializeAccessibilityNodeInfo(host, info);
@@ -111,6 +126,10 @@
                     && mRecyclerViewDelegate.mRecyclerView.getLayoutManager() != null) {
                 mRecyclerViewDelegate.mRecyclerView.getLayoutManager()
                         .onInitializeAccessibilityNodeInfoForItem(host, info);
+                AccessibilityDelegateCompat originalDelegate = mOriginalItemDelegates.get(host);
+                if (originalDelegate != null) {
+                    originalDelegate.onInitializeAccessibilityNodeInfo(host, info);
+                }
             }
         }
 
@@ -121,8 +140,14 @@
             }
             if (!mRecyclerViewDelegate.shouldIgnore()
                     && mRecyclerViewDelegate.mRecyclerView.getLayoutManager() != null) {
-                return mRecyclerViewDelegate.mRecyclerView.getLayoutManager()
-                        .performAccessibilityActionForItem(host, action, args);
+                AccessibilityDelegateCompat originalDelegate = mOriginalItemDelegates.get(host);
+                if (originalDelegate != null
+                        && originalDelegate.performAccessibilityAction(host, action, args)) {
+                    return true;
+                } else {
+                    return mRecyclerViewDelegate.mRecyclerView.getLayoutManager()
+                            .performAccessibilityActionForItem(host, action, args);
+                }
             }
             return false;
         }
diff --git a/room/benchmark/build.gradle b/room/benchmark/build.gradle
index 77c626c..b13c3b9 100644
--- a/room/benchmark/build.gradle
+++ b/room/benchmark/build.gradle
@@ -20,6 +20,7 @@
     id("com.android.library")
     id("kotlin-android")
     id("kotlin-kapt")
+    id("androidx.benchmark")
 }
 
 dependencies {
@@ -30,7 +31,7 @@
     androidTestImplementation(project(":sqlite:sqlite"))
     androidTestImplementation(project(":sqlite:sqlite-framework"))
     androidTestImplementation(ARCH_CORE_RUNTIME)
-    androidTestImplementation(project(":benchmark"))
+    androidTestImplementation(project(":benchmark:benchmark-junit4"))
     androidTestImplementation(RX_JAVA)
     androidTestImplementation(JUNIT)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/room/benchmark/src/androidTest/java/androidx/room/benchmark/InvalidationTrackerBenchmark.kt b/room/benchmark/src/androidTest/java/androidx/room/benchmark/InvalidationTrackerBenchmark.kt
index 7537d99..861da78 100644
--- a/room/benchmark/src/androidTest/java/androidx/room/benchmark/InvalidationTrackerBenchmark.kt
+++ b/room/benchmark/src/androidTest/java/androidx/room/benchmark/InvalidationTrackerBenchmark.kt
@@ -17,8 +17,8 @@
 package androidx.room.benchmark
 
 import android.os.Build
-import androidx.benchmark.BenchmarkRule
-import androidx.benchmark.measureRepeated
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
 import androidx.room.Dao
 import androidx.room.Database
 import androidx.room.Entity
diff --git a/room/benchmark/src/androidTest/java/androidx/room/benchmark/RelationBenchmark.kt b/room/benchmark/src/androidTest/java/androidx/room/benchmark/RelationBenchmark.kt
index ac4c427..0fea703 100644
--- a/room/benchmark/src/androidTest/java/androidx/room/benchmark/RelationBenchmark.kt
+++ b/room/benchmark/src/androidTest/java/androidx/room/benchmark/RelationBenchmark.kt
@@ -17,8 +17,8 @@
 package androidx.room.benchmark
 
 import android.os.Build
-import androidx.benchmark.BenchmarkRule
-import androidx.benchmark.measureRepeated
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
 import androidx.room.Dao
 import androidx.room.Database
 import androidx.room.Embedded
diff --git a/room/compiler/src/main/kotlin/androidx/room/ext/element_ext.kt b/room/compiler/src/main/kotlin/androidx/room/ext/element_ext.kt
index e47b44d..1e02fad 100644
--- a/room/compiler/src/main/kotlin/androidx/room/ext/element_ext.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/ext/element_ext.kt
@@ -32,6 +32,7 @@
 import javax.lang.model.element.Modifier
 import javax.lang.model.element.TypeElement
 import javax.lang.model.element.VariableElement
+import javax.lang.model.type.ExecutableType
 import javax.lang.model.type.TypeKind
 import javax.lang.model.type.TypeMirror
 import javax.lang.model.type.WildcardType
@@ -380,12 +381,12 @@
 
 /**
  * Finds the Kotlin's suspend function return type by inspecting the type param of the Continuation
- * parameter of the function. This method assumes the executable element is a suspend function.
+ * parameter of the function. This method assumes the executable type is a suspend function.
  * @see KotlinMetadataElement.isSuspendFunction
  */
-fun ExecutableElement.getSuspendFunctionReturnType(): TypeMirror {
+fun ExecutableType.getSuspendFunctionReturnType(): TypeMirror {
     // the continuation parameter is always the last parameter of a suspend function and it only has
     // one type parameter, e.g Continuation<? super T>
-    val typeParam = MoreTypes.asDeclared(parameters.last().asType()).typeArguments.first()
+    val typeParam = MoreTypes.asDeclared(parameterTypes.last()).typeArguments.first()
     return typeParam.extendsBoundOrSelf() // reduce the type param
 }
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt b/room/compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt
index 70e30ff..6e7a114 100644
--- a/room/compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt
@@ -165,6 +165,10 @@
     val UNIT = ClassName.get("kotlin", "Unit")
     val CONTINUATION = ClassName.get("kotlin.coroutines", "Continuation")
     val COROUTINE_SCOPE = ClassName.get("kotlinx.coroutines", "CoroutineScope")
+    val CHANNEL = ClassName.get("kotlinx.coroutines.channels", "Channel")
+    val RECEIVE_CHANNEL = ClassName.get("kotlinx.coroutines.channels", "ReceiveChannel")
+    val SEND_CHANNEL = ClassName.get("kotlinx.coroutines.channels", "SendChannel")
+    val FLOW = ClassName.get("kotlinx.coroutines.flow", "Flow")
 }
 
 fun TypeName.defaultValue(): String {
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/MethodProcessorDelegate.kt b/room/compiler/src/main/kotlin/androidx/room/processor/MethodProcessorDelegate.kt
index 4d8cfa7..6274c80 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/MethodProcessorDelegate.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/MethodProcessorDelegate.kt
@@ -181,7 +181,10 @@
         }
     }
 
-    override fun extractReturnType() = executableElement.getSuspendFunctionReturnType()
+    override fun extractReturnType(): TypeMirror {
+        val asMember = context.processingEnv.typeUtils.asMemberOf(containing, executableElement)
+        return MoreTypes.asExecutable(asMember).getSuspendFunctionReturnType()
+    }
 
     override fun extractParams() =
         executableElement.parameters.filterNot { it == continuationParam }
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt b/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
index e313569..d331aa1 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
@@ -21,6 +21,7 @@
 import androidx.room.Query
 import androidx.room.RawQuery
 import androidx.room.Update
+import androidx.room.ext.KotlinTypeNames
 import androidx.room.ext.RoomTypeNames
 import androidx.room.ext.SupportDbTypeNames
 import androidx.room.parser.QueryType
@@ -725,4 +726,8 @@
                 " (https://bugs.openjdk.java.net/browse/JDK-8007720)" +
                 " that prevents Room from being incremental." +
                 " Consider using JDK 11+ or the embedded JDK shipped with Android Studio 3.5+."
+
+    fun invalidChannelType(typeName: String) = "'$typeName' is not supported as a return type. " +
+            "Instead declare return type as ${KotlinTypeNames.FLOW} and use Flow transforming " +
+            "functions that converts the Flow into a Channel."
 }
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt b/room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
index 8153400..d5a8ce3 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
@@ -28,6 +28,7 @@
 import androidx.room.processor.EntityProcessor
 import androidx.room.processor.FieldProcessor
 import androidx.room.processor.PojoProcessor
+import androidx.room.solver.binderprovider.CoroutineFlowResultBinderProvider
 import androidx.room.solver.binderprovider.CursorQueryResultBinderProvider
 import androidx.room.solver.binderprovider.DataSourceFactoryQueryResultBinderProvider
 import androidx.room.solver.binderprovider.DataSourceQueryResultBinderProvider
@@ -81,6 +82,7 @@
 import androidx.room.solver.types.BoxedBooleanToBoxedIntConverter
 import androidx.room.solver.types.BoxedPrimitiveColumnTypeAdapter
 import androidx.room.solver.types.ByteArrayColumnTypeAdapter
+import androidx.room.solver.types.ByteBufferColumnTypeAdapter
 import androidx.room.solver.types.ColumnTypeAdapter
 import androidx.room.solver.types.CompositeAdapter
 import androidx.room.solver.types.CompositeTypeConverter
@@ -155,6 +157,7 @@
                     .forEach(::addColumnAdapter)
             addColumnAdapter(StringColumnTypeAdapter(context.processingEnv))
             addColumnAdapter(ByteArrayColumnTypeAdapter(context.processingEnv))
+            addColumnAdapter(ByteBufferColumnTypeAdapter(context.processingEnv))
             PrimitiveBooleanToIntConverter.create(context.processingEnv).forEach(::addTypeConverter)
             BoxedBooleanToBoxedIntConverter.create(context.processingEnv)
                     .forEach(::addTypeConverter)
@@ -173,6 +176,7 @@
             RxSingleQueryResultBinderProvider(context),
             DataSourceQueryResultBinderProvider(context),
             DataSourceFactoryQueryResultBinderProvider(context),
+            CoroutineFlowResultBinderProvider(context),
             InstantQueryResultBinderProvider(context)
     )
 
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/binderprovider/CoroutineFlowResultBinderProvider.kt b/room/compiler/src/main/kotlin/androidx/room/solver/binderprovider/CoroutineFlowResultBinderProvider.kt
new file mode 100644
index 0000000..b419a57
--- /dev/null
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/binderprovider/CoroutineFlowResultBinderProvider.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.room.solver.binderprovider
+
+import androidx.room.ext.KotlinTypeNames
+import androidx.room.ext.RoomCoroutinesTypeNames
+import androidx.room.ext.typeName
+import androidx.room.parser.ParsedQuery
+import androidx.room.processor.Context
+import androidx.room.processor.ProcessorErrors
+import androidx.room.solver.QueryResultBinderProvider
+import androidx.room.solver.query.result.CoroutineFlowResultBinder
+import androidx.room.solver.query.result.QueryResultBinder
+import javax.lang.model.type.DeclaredType
+
+class CoroutineFlowResultBinderProvider(val context: Context) : QueryResultBinderProvider {
+
+    companion object {
+        val CHANNEL_TYPE_NAMES = listOf(
+            KotlinTypeNames.CHANNEL,
+            KotlinTypeNames.SEND_CHANNEL,
+            KotlinTypeNames.RECEIVE_CHANNEL
+        )
+    }
+
+    private val hasCoroutinesArtifact by lazy {
+        context.processingEnv.elementUtils
+            .getTypeElement(RoomCoroutinesTypeNames.COROUTINES_ROOM.toString()) != null
+    }
+
+    override fun provide(declared: DeclaredType, query: ParsedQuery): QueryResultBinder {
+        val typeArg = declared.typeArguments.first()
+        val adapter = context.typeAdapterStore.findQueryResultAdapter(typeArg, query)
+        val tableNames = ((adapter?.accessedTableNames() ?: emptyList()) +
+                query.tables.map { it.name }).toSet()
+        if (tableNames.isEmpty()) {
+            context.logger.e(ProcessorErrors.OBSERVABLE_QUERY_NOTHING_TO_OBSERVE)
+        }
+        return CoroutineFlowResultBinder(typeArg, tableNames, adapter)
+    }
+
+    override fun matches(declared: DeclaredType): Boolean {
+        if (declared.typeArguments.size != 1) {
+            return false
+        }
+        val typeName = context.processingEnv.typeUtils.erasure(declared).typeName()
+        if (typeName in CHANNEL_TYPE_NAMES) {
+            context.logger.e(ProcessorErrors.invalidChannelType(typeName.toString()))
+            return false
+        }
+        val match = typeName == KotlinTypeNames.FLOW
+        if (match && !hasCoroutinesArtifact) {
+            context.logger.e(ProcessorErrors.MISSING_ROOM_COROUTINE_ARTIFACT)
+        }
+        return match
+    }
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
new file mode 100644
index 0000000..bc2fb8e
--- /dev/null
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.room.solver.query.result
+
+import androidx.room.ext.AndroidTypeNames
+import androidx.room.ext.CallableTypeSpecBuilder
+import androidx.room.ext.L
+import androidx.room.ext.N
+import androidx.room.ext.RoomCoroutinesTypeNames
+import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.T
+import androidx.room.ext.arrayTypeName
+import androidx.room.ext.typeName
+import androidx.room.solver.CodeGenScope
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.MethodSpec
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Binds the result of a of a Kotlin Coroutine Flow<T>
+ */
+class CoroutineFlowResultBinder(
+    val typeArg: TypeMirror,
+    val tableNames: Set<String>,
+    adapter: QueryResultAdapter?
+) : QueryResultBinder(adapter) {
+
+    override fun convertAndReturn(
+        roomSQLiteQueryVar: String,
+        canReleaseQuery: Boolean,
+        dbField: FieldSpec,
+        inTransaction: Boolean,
+        scope: CodeGenScope
+    ) {
+        val callableImpl = CallableTypeSpecBuilder(typeArg.typeName()) {
+            createRunQueryAndReturnStatements(
+                builder = this,
+                roomSQLiteQueryVar = roomSQLiteQueryVar,
+                canReleaseQuery = canReleaseQuery,
+                dbField = dbField,
+                inTransaction = inTransaction,
+                scope = scope)
+        }.build()
+
+        scope.builder().apply {
+            val tableNamesList = tableNames.joinToString(",") { "\"$it\"" }
+            addStatement(
+                "return $T.createFlow($N, $L, new $T{$L}, $L)",
+                RoomCoroutinesTypeNames.COROUTINES_ROOM,
+                dbField,
+                if (inTransaction) "true" else "false",
+                String::class.arrayTypeName(),
+                tableNamesList,
+                callableImpl)
+        }
+    }
+
+    private fun createRunQueryAndReturnStatements(
+        builder: MethodSpec.Builder,
+        roomSQLiteQueryVar: String,
+        canReleaseQuery: Boolean,
+        dbField: FieldSpec,
+        inTransaction: Boolean,
+        scope: CodeGenScope
+    ) {
+        val transactionWrapper = if (inTransaction) {
+            builder.transactionWrapper(dbField)
+        } else {
+            null
+        }
+        val shouldCopyCursor = adapter?.shouldCopyCursor() == true
+        val outVar = scope.getTmpVar("_result")
+        val cursorVar = scope.getTmpVar("_cursor")
+        transactionWrapper?.beginTransactionWithControlFlow()
+        builder.apply {
+            addStatement("final $T $L = $T.query($N, $L, $L)",
+                AndroidTypeNames.CURSOR,
+                cursorVar,
+                RoomTypeNames.DB_UTIL,
+                dbField,
+                roomSQLiteQueryVar,
+                if (shouldCopyCursor) "true" else "false")
+            beginControlFlow("try").apply {
+                val adapterScope = scope.fork()
+                adapter?.convert(outVar, cursorVar, adapterScope)
+                addCode(adapterScope.builder().build())
+                transactionWrapper?.commitTransaction()
+                addStatement("return $L", outVar)
+            }
+            nextControlFlow("finally").apply {
+                addStatement("$L.close()", cursorVar)
+                if (canReleaseQuery) {
+                    addStatement("$L.release()", roomSQLiteQueryVar)
+                }
+            }
+            endControlFlow()
+        }
+        transactionWrapper?.endTransactionWithControlFlow()
+    }
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/types/ByteBufferColumnTypeAdapter.kt b/room/compiler/src/main/kotlin/androidx/room/solver/types/ByteBufferColumnTypeAdapter.kt
new file mode 100644
index 0000000..95dfb99
--- /dev/null
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/types/ByteBufferColumnTypeAdapter.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.room.solver.types
+
+import androidx.room.ext.L
+import androidx.room.ext.T
+import androidx.room.parser.SQLTypeAffinity
+import androidx.room.solver.CodeGenScope
+import com.squareup.javapoet.TypeName
+import java.nio.ByteBuffer
+import javax.annotation.processing.ProcessingEnvironment
+
+class ByteBufferColumnTypeAdapter(env: ProcessingEnvironment) : ColumnTypeAdapter(
+    out = env.elementUtils.getTypeElement("java.nio.ByteBuffer").asType(),
+    typeAffinity = SQLTypeAffinity.BLOB
+) {
+    override fun readFromCursor(
+        outVarName: String,
+        cursorVarName: String,
+        indexVarName: String,
+        scope: CodeGenScope
+    ) {
+        scope.builder()
+            .addStatement("$L = $T.wrap($L.getBlob($L))",
+                outVarName, TypeName.get(ByteBuffer::class.java), cursorVarName, indexVarName)
+    }
+
+    override fun bindToStmt(
+        stmtName: String,
+        indexVarName: String,
+        valueVarName: String,
+        scope: CodeGenScope
+    ) {
+        scope.builder().apply {
+            beginControlFlow("if ($L == null)", valueVarName)
+                .addStatement("$L.bindNull($L)", stmtName, indexVarName)
+            nextControlFlow("else")
+                .addStatement("$L.bindBlob($L, $L.array())", stmtName, indexVarName, valueVarName)
+            endControlFlow()
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt b/room/compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
index 3fca29d..ac182c2 100644
--- a/room/compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
@@ -38,15 +38,14 @@
 import androidx.room.writer.QueryWriter
 import androidx.room.writer.RelationCollectorMethodWriter
 import com.google.auto.common.MoreTypes
-import com.squareup.javapoet.ArrayTypeName
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.CodeBlock
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
 import stripNonJava
+import java.nio.ByteBuffer
 import java.util.ArrayList
 import java.util.HashSet
-import javax.lang.model.type.TypeKind
 import javax.lang.model.type.TypeMirror
 
 /**
@@ -160,15 +159,21 @@
                 keyTypeName
             }
             val tmpVar = scope.getTmpVar("_tmpKey")
-            if (relation.parentField.nonNull) {
-                addStatement("final $T $L = $L.$L($L)",
+            fun addKeyReadStatement() {
+                if (keyTypeName == TypeName.get(ByteBuffer::class.java)) {
+                    addStatement("final $T $L = $T.wrap($L.$L($L))",
+                        keyType, tmpVar, keyTypeName, cursorVarName, cursorGetter, indexVar)
+                } else {
+                    addStatement("final $T $L = $L.$L($L)",
                         keyType, tmpVar, cursorVarName, cursorGetter, indexVar)
+                }
                 this.postRead(tmpVar)
+            }
+            if (relation.parentField.nonNull) {
+                addKeyReadStatement()
             } else {
                 beginControlFlow("if (!$L.isNull($L))", cursorVarName, indexVar).apply {
-                    addStatement("final $T $L = $L.$L($L)",
-                            keyType, tmpVar, cursorVarName, cursorGetter, indexVar)
-                    this.postRead(tmpVar)
+                    addKeyReadStatement()
                 }
                 endControlFlow()
             }
@@ -219,92 +224,13 @@
             relations: List<Relation>
         ): List<RelationCollector> {
             return relations.map { relation ->
-                // decide on the affinity
                 val context = baseContext.fork(
                     element = relation.field.element,
                     forceSuppressedWarnings = setOf(Warning.CURSOR_MISMATCH))
-
-                fun checkAffinity(
-                    first: SQLTypeAffinity?,
-                    second: SQLTypeAffinity?,
-                    onAffinityMismatch: () -> Unit
-                ) = if (first != null && first == second) {
-                    first
-                } else {
-                    onAffinityMismatch()
-                    SQLTypeAffinity.TEXT
-                }
-
-                val parentAffinity = relation.parentField.cursorValueReader?.affinity()
-                val childAffinity = relation.entityField.cursorValueReader?.affinity()
-                val junctionParentAffinity =
-                    relation.junction?.parentField?.cursorValueReader?.affinity()
-                val junctionChildAffinity =
-                    relation.junction?.entityField?.cursorValueReader?.affinity()
-                val affinity = if (relation.junction != null) {
-                    checkAffinity(childAffinity, junctionChildAffinity) {
-                        context.logger.w(Warning.RELATION_TYPE_MISMATCH, relation.field.element,
-                            relationJunctionChildAffinityMismatch(
-                                childColumn = relation.entityField.columnName,
-                                junctionChildColumn = relation.junction.entityField.columnName,
-                                childAffinity = childAffinity,
-                                junctionChildAffinity = junctionChildAffinity))
-                    }
-                    checkAffinity(parentAffinity, junctionParentAffinity) {
-                        context.logger.w(Warning.RELATION_TYPE_MISMATCH, relation.field.element,
-                            relationJunctionParentAffinityMismatch(
-                                parentColumn = relation.parentField.columnName,
-                                junctionParentColumn = relation.junction.parentField.columnName,
-                                parentAffinity = parentAffinity,
-                                junctionParentAffinity = junctionParentAffinity))
-                    }
-                } else {
-                    checkAffinity(parentAffinity, childAffinity) {
-                        context.logger.w(Warning.RELATION_TYPE_MISMATCH, relation.field.element,
-                            relationAffinityMismatch(
-                                parentColumn = relation.parentField.columnName,
-                                childColumn = relation.entityField.columnName,
-                                parentAffinity = parentAffinity,
-                                childAffinity = childAffinity))
-                    }
-                }
+                val affinity = affinityFor(context, relation)
                 val keyType = keyTypeFor(context, affinity)
-                val (relationTypeName, isRelationCollection) =
-                    if (relation.field.typeName is ParameterizedTypeName) {
-                        val paramType = relation.field.typeName as ParameterizedTypeName
-                        val paramTypeName = if (paramType.rawType == CommonTypeNames.LIST) {
-                            ParameterizedTypeName.get(ClassName.get(ArrayList::class.java),
-                                relation.pojoTypeName)
-                        } else if (paramType.rawType == CommonTypeNames.SET) {
-                            ParameterizedTypeName.get(ClassName.get(HashSet::class.java),
-                                relation.pojoTypeName)
-                        } else {
-                            ParameterizedTypeName.get(ClassName.get(ArrayList::class.java),
-                                relation.pojoTypeName)
-                        }
-                        paramTypeName to true
-                    } else {
-                        relation.pojoTypeName to false
-                    }
-
-                val canUseLongSparseArray = context.processingEnv.elementUtils
-                        .getTypeElement(CollectionTypeNames.LONG_SPARSE_ARRAY.toString()) != null
-                val canUseArrayMap = context.processingEnv.elementUtils
-                        .getTypeElement(CollectionTypeNames.ARRAY_MAP.toString()) != null
-                val tmpMapType = when {
-                    canUseLongSparseArray && affinity == SQLTypeAffinity.INTEGER -> {
-                        ParameterizedTypeName.get(CollectionTypeNames.LONG_SPARSE_ARRAY,
-                                relationTypeName)
-                    }
-                    canUseArrayMap -> {
-                        ParameterizedTypeName.get(CollectionTypeNames.ARRAY_MAP,
-                                keyType, relationTypeName)
-                    }
-                    else -> {
-                        ParameterizedTypeName.get(ClassName.get(java.util.HashMap::class.java),
-                                keyType, relationTypeName)
-                    }
-                }
+                val (relationTypeName, isRelationCollection) = relationTypeFor(relation)
+                val tmpMapType = temporaryMapTypeFor(context, affinity, keyType, relationTypeName)
 
                 val loadAllQuery = relation.createLoadAllSql()
                 val parsedQuery = SqlParser.parse(loadAllQuery)
@@ -340,7 +266,7 @@
                             sqlName = RelationCollectorMethodWriter.KEY_SET_VARIABLE,
                             type = keySet,
                             queryParamAdapter = context.typeAdapterStore.findQueryParameterAdapter(
-                                    keySet)
+                                keySet)
                     )
                 }
 
@@ -392,26 +318,121 @@
             }.filterNotNull()
         }
 
+        // Gets and check the affinity of the relating columns.
+        private fun affinityFor(context: Context, relation: Relation): SQLTypeAffinity {
+            fun checkAffinity(
+                first: SQLTypeAffinity?,
+                second: SQLTypeAffinity?,
+                onAffinityMismatch: () -> Unit
+            ) = if (first != null && first == second) {
+                first
+            } else {
+                onAffinityMismatch()
+                SQLTypeAffinity.TEXT
+            }
+
+            val parentAffinity = relation.parentField.cursorValueReader?.affinity()
+            val childAffinity = relation.entityField.cursorValueReader?.affinity()
+            val junctionParentAffinity =
+                relation.junction?.parentField?.cursorValueReader?.affinity()
+            val junctionChildAffinity =
+                relation.junction?.entityField?.cursorValueReader?.affinity()
+            return if (relation.junction != null) {
+                checkAffinity(childAffinity, junctionChildAffinity) {
+                    context.logger.w(Warning.RELATION_TYPE_MISMATCH, relation.field.element,
+                        relationJunctionChildAffinityMismatch(
+                            childColumn = relation.entityField.columnName,
+                            junctionChildColumn = relation.junction.entityField.columnName,
+                            childAffinity = childAffinity,
+                            junctionChildAffinity = junctionChildAffinity))
+                }
+                checkAffinity(parentAffinity, junctionParentAffinity) {
+                    context.logger.w(Warning.RELATION_TYPE_MISMATCH, relation.field.element,
+                        relationJunctionParentAffinityMismatch(
+                            parentColumn = relation.parentField.columnName,
+                            junctionParentColumn = relation.junction.parentField.columnName,
+                            parentAffinity = parentAffinity,
+                            junctionParentAffinity = junctionParentAffinity))
+                }
+            } else {
+                checkAffinity(parentAffinity, childAffinity) {
+                    context.logger.w(Warning.RELATION_TYPE_MISMATCH, relation.field.element,
+                        relationAffinityMismatch(
+                            parentColumn = relation.parentField.columnName,
+                            childColumn = relation.entityField.columnName,
+                            parentAffinity = parentAffinity,
+                            childAffinity = childAffinity))
+                }
+            }
+        }
+
+        // Gets the resulting relation type name. (i.e. the Pojo's @Relation field type name.)
+        private fun relationTypeFor(relation: Relation) =
+            if (relation.field.typeName is ParameterizedTypeName) {
+                val paramType = relation.field.typeName as ParameterizedTypeName
+                val paramTypeName = if (paramType.rawType == CommonTypeNames.LIST) {
+                    ParameterizedTypeName.get(ClassName.get(ArrayList::class.java),
+                        relation.pojoTypeName)
+                } else if (paramType.rawType == CommonTypeNames.SET) {
+                    ParameterizedTypeName.get(ClassName.get(HashSet::class.java),
+                        relation.pojoTypeName)
+                } else {
+                    ParameterizedTypeName.get(ClassName.get(ArrayList::class.java),
+                        relation.pojoTypeName)
+                }
+                paramTypeName to true
+            } else {
+                relation.pojoTypeName to false
+            }
+
+        // Gets the type name of the temporary key map.
+        private fun temporaryMapTypeFor(
+            context: Context,
+            affinity: SQLTypeAffinity,
+            keyType: TypeName,
+            relationTypeName: TypeName
+        ): ParameterizedTypeName {
+            val canUseLongSparseArray = context.processingEnv.elementUtils
+                .getTypeElement(CollectionTypeNames.LONG_SPARSE_ARRAY.toString()) != null
+            val canUseArrayMap = context.processingEnv.elementUtils
+                .getTypeElement(CollectionTypeNames.ARRAY_MAP.toString()) != null
+            return when {
+                canUseLongSparseArray && affinity == SQLTypeAffinity.INTEGER -> {
+                    ParameterizedTypeName.get(CollectionTypeNames.LONG_SPARSE_ARRAY,
+                        relationTypeName)
+                }
+                canUseArrayMap -> {
+                    ParameterizedTypeName.get(CollectionTypeNames.ARRAY_MAP,
+                        keyType, relationTypeName)
+                }
+                else -> {
+                    ParameterizedTypeName.get(ClassName.get(java.util.HashMap::class.java),
+                        keyType, relationTypeName)
+                }
+            }
+        }
+
+        // Gets the type mirror of the relationship key.
         private fun keyTypeMirrorFor(context: Context, affinity: SQLTypeAffinity): TypeMirror {
-            val types = context.processingEnv.typeUtils
             val elements = context.processingEnv.elementUtils
             return when (affinity) {
                 SQLTypeAffinity.INTEGER -> elements.getTypeElement("java.lang.Long").asType()
                 SQLTypeAffinity.REAL -> elements.getTypeElement("java.lang.Double").asType()
                 SQLTypeAffinity.TEXT -> context.COMMON_TYPES.STRING
-                SQLTypeAffinity.BLOB -> types.getArrayType(types.getPrimitiveType(TypeKind.BYTE))
+                SQLTypeAffinity.BLOB -> elements.getTypeElement("java.nio.ByteBuffer").asType()
                 else -> {
                     context.COMMON_TYPES.STRING
                 }
             }
         }
 
+        // Gets the type name of the relationship key.
         private fun keyTypeFor(context: Context, affinity: SQLTypeAffinity): TypeName {
             return when (affinity) {
                 SQLTypeAffinity.INTEGER -> TypeName.LONG.box()
                 SQLTypeAffinity.REAL -> TypeName.DOUBLE.box()
                 SQLTypeAffinity.TEXT -> TypeName.get(String::class.java)
-                SQLTypeAffinity.BLOB -> ArrayTypeName.of(TypeName.BYTE)
+                SQLTypeAffinity.BLOB -> TypeName.get(ByteBuffer::class.java)
                 else -> {
                     // no affinity select from type
                     context.COMMON_TYPES.STRING.typeName()
diff --git a/room/compiler/src/test/data/common/input/coroutines/Channel.java b/room/compiler/src/test/data/common/input/coroutines/Channel.java
new file mode 100644
index 0000000..092782a
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/coroutines/Channel.java
@@ -0,0 +1,21 @@
+/*
+ * 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 kotlinx.coroutines.channels;
+
+public interface Channel<T> extends SendChannel<T>, ReceiveChannel<T> {
+
+}
\ No newline at end of file
diff --git a/room/compiler/src/test/data/common/input/coroutines/ReceiveChannel.java b/room/compiler/src/test/data/common/input/coroutines/ReceiveChannel.java
new file mode 100644
index 0000000..a822127
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/coroutines/ReceiveChannel.java
@@ -0,0 +1,21 @@
+/*
+ * 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 kotlinx.coroutines.channels;
+
+public interface ReceiveChannel<T> {
+
+}
\ No newline at end of file
diff --git a/room/compiler/src/test/data/common/input/coroutines/SendChannel.java b/room/compiler/src/test/data/common/input/coroutines/SendChannel.java
new file mode 100644
index 0000000..a8c4b11
--- /dev/null
+++ b/room/compiler/src/test/data/common/input/coroutines/SendChannel.java
@@ -0,0 +1,21 @@
+/*
+ * 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 kotlinx.coroutines.channels;
+
+public interface SendChannel<T> {
+
+}
\ No newline at end of file
diff --git a/room/compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt b/room/compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
index 47d039d..aee9801 100644
--- a/room/compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
@@ -25,6 +25,7 @@
 import androidx.room.Relation
 import androidx.room.Transaction
 import androidx.room.ext.CommonTypeNames
+import androidx.room.ext.KotlinTypeNames
 import androidx.room.ext.LifecyclesTypeNames
 import androidx.room.ext.PagingTypeNames
 import androidx.room.ext.hasAnnotation
@@ -73,6 +74,7 @@
 import javax.lang.model.type.DeclaredType
 import javax.lang.model.type.TypeKind.INT
 import javax.lang.model.type.TypeMirror
+import javax.tools.JavaFileObject
 
 @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
 @RunWith(Parameterized::class)
@@ -560,6 +562,48 @@
     }
 
     @Test
+    fun testBadChannelReturnForQuery() {
+        singleQueryMethod<QueryMethod>(
+            """
+                @Query("select * from user")
+                abstract ${KotlinTypeNames.CHANNEL}<User> getUsersChannel();
+                """,
+            jfos = listOf(COMMON.CHANNEL)
+        ) { _, _ ->
+        }.failsToCompile()
+            .withErrorContaining(ProcessorErrors.invalidChannelType(
+                KotlinTypeNames.CHANNEL.toString()))
+    }
+
+    @Test
+    fun testBadSendChannelReturnForQuery() {
+        singleQueryMethod<QueryMethod>(
+            """
+                @Query("select * from user")
+                abstract ${KotlinTypeNames.SEND_CHANNEL}<User> getUsersChannel();
+                """,
+            jfos = listOf(COMMON.SEND_CHANNEL)
+        ) { _, _ ->
+        }.failsToCompile()
+            .withErrorContaining(ProcessorErrors.invalidChannelType(
+                KotlinTypeNames.SEND_CHANNEL.toString()))
+    }
+
+    @Test
+    fun testBadReceiveChannelReturnForQuery() {
+        singleQueryMethod<QueryMethod>(
+            """
+                @Query("select * from user")
+                abstract ${KotlinTypeNames.RECEIVE_CHANNEL}<User> getUsersChannel();
+                """,
+            jfos = listOf(COMMON.RECEIVE_CHANNEL)
+        ) { _, _ ->
+        }.failsToCompile()
+            .withErrorContaining(ProcessorErrors.invalidChannelType(
+                KotlinTypeNames.RECEIVE_CHANNEL.toString()))
+    }
+
+    @Test
     fun query_detectTransaction_select() {
         singleQueryMethod<ReadQueryMethod>(
                 """
@@ -857,6 +901,7 @@
 
     private fun <T : QueryMethod> singleQueryMethod(
         vararg input: String,
+        jfos: Iterable<JavaFileObject> = emptyList(),
         handler: (T, TestInvocation) -> Unit
     ): CompileTester {
         return assertAbout(JavaSourcesSubjectFactory.javaSources())
@@ -866,7 +911,7 @@
                         "foo.bar.MyClass",
                         DAO_PREFIX + input.joinToString("\n") + DAO_SUFFIX
                     ), COMMON.LIVE_DATA, COMMON.COMPUTABLE_LIVE_DATA, COMMON.USER, COMMON.BOOK
-                )
+                ) + jfos
             )
             .processedWith(TestProcessor.builder()
                 .forAnnotations(
diff --git a/room/compiler/src/test/kotlin/androidx/room/testing/test_util.kt b/room/compiler/src/test/kotlin/androidx/room/testing/test_util.kt
index 1bf74f5..8e2c368 100644
--- a/room/compiler/src/test/kotlin/androidx/room/testing/test_util.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/testing/test_util.kt
@@ -17,6 +17,7 @@
 import androidx.room.DatabaseView
 import androidx.room.Entity
 import androidx.room.ext.GuavaUtilConcurrentTypeNames
+import androidx.room.ext.KotlinTypeNames
 import androidx.room.ext.LifecyclesTypeNames
 import androidx.room.ext.PagingTypeNames
 import androidx.room.ext.ReactiveStreamsTypeNames
@@ -143,6 +144,21 @@
         loadJavaCode("common/input/GuavaRoom.java",
             RoomGuavaTypeNames.GUAVA_ROOM.toString())
     }
+
+    val CHANNEL by lazy {
+        loadJavaCode("common/input/coroutines/Channel.java",
+            KotlinTypeNames.CHANNEL.toString())
+    }
+
+    val SEND_CHANNEL by lazy {
+        loadJavaCode("common/input/coroutines/SendChannel.java",
+            KotlinTypeNames.SEND_CHANNEL.toString())
+    }
+
+    val RECEIVE_CHANNEL by lazy {
+        loadJavaCode("common/input/coroutines/ReceiveChannel.java",
+            KotlinTypeNames.RECEIVE_CHANNEL.toString())
+    }
 }
 fun testCodeGenScope(): CodeGenScope {
     return CodeGenScope(Mockito.mock(ClassWriter::class.java))
diff --git a/room/integration-tests/kotlintestapp/build.gradle b/room/integration-tests/kotlintestapp/build.gradle
index 61bfc6d..d247001 100644
--- a/room/integration-tests/kotlintestapp/build.gradle
+++ b/room/integration-tests/kotlintestapp/build.gradle
@@ -47,7 +47,7 @@
     implementation(project(":arch:core-runtime"))
     implementation(project(":lifecycle:lifecycle-livedata"))
     implementation(KOTLIN_STDLIB)
-    implementation(KOTLIN_COROUTINES)
+    implementation(KOTLIN_COROUTINES_PREVIEW)
     kaptAndroidTest project(":room:room-compiler")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BaseDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BaseDao.kt
index 1653a0e..3419b5b 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BaseDao.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BaseDao.kt
@@ -19,7 +19,9 @@
 import androidx.room.Delete
 import androidx.room.Insert
 import androidx.room.OnConflictStrategy
+import androidx.room.RawQuery
 import androidx.room.Update
+import androidx.sqlite.db.SupportSQLiteQuery
 
 interface BaseDao<T> {
 
@@ -46,4 +48,13 @@
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     suspend fun suspendInsert(t: T)
+
+    @Update
+    suspend fun suspendUpdate(t: T)
+
+    @Delete
+    suspend fun suspendDelete(t: T)
+
+    @RawQuery
+    suspend fun rawQuery(query: SupportSQLiteQuery): List<T>
 }
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt
index 290bdb1..6cadb2f 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt
@@ -43,6 +43,7 @@
 import io.reactivex.Flowable
 import io.reactivex.Maybe
 import io.reactivex.Single
+import kotlinx.coroutines.flow.Flow
 import java.util.Date
 
 @Dao
@@ -376,4 +377,11 @@
             insertBookSuspend(book)
         }
     }
+
+    @Query("SELECT * FROM book")
+    fun getBooksFlow(): Flow<List<Book>>
+
+    @Transaction
+    @Query("SELECT * FROM book")
+    fun getBooksFlowInTransaction(): Flow<List<Book>>
 }
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt
new file mode 100644
index 0000000..0abc393
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt
@@ -0,0 +1,257 @@
+/*
+ * 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.room.integration.kotlintestapp.test
+
+import androidx.room.integration.kotlintestapp.vo.Book
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.async
+import kotlinx.coroutines.cancelAndJoin
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.produceIn
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
+import org.junit.After
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Phaser
+import java.util.concurrent.TimeUnit
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@FlowPreview
+@ExperimentalCoroutinesApi
+class FlowQueryTest : TestDatabaseTest() {
+
+    @After
+    fun teardown() {
+        // At the end of all tests, query executor should be idle.
+        countingTaskExecutorRule.drainTasks(500, TimeUnit.MILLISECONDS)
+        assertThat(countingTaskExecutorRule.isIdle).isTrue()
+    }
+
+    @Test
+    fun collectBooks_takeOne() = runBlocking {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1, TestUtil.BOOK_2)
+
+        booksDao.getBooksFlow().take(1).collect {
+            assertThat(it)
+                .isEqualTo(listOf(TestUtil.BOOK_1, TestUtil.BOOK_2))
+        }
+    }
+
+    @Test
+    fun collectBooks_async() = runBlocking {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1, TestUtil.BOOK_2)
+
+        val latch = CountDownLatch(1)
+        val job = async(Dispatchers.IO) {
+            booksDao.getBooksFlow().collect {
+                assertThat(it)
+                    .isEqualTo(listOf(TestUtil.BOOK_1, TestUtil.BOOK_2))
+                latch.countDown()
+            }
+        }
+
+        latch.await()
+        job.cancelAndJoin()
+    }
+
+    @Test
+    fun receiveBooks_async_update() = runBlocking {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1, TestUtil.BOOK_2)
+
+        val barrier = Phaser(2)
+        val results = mutableListOf<List<Book>>()
+        val job = async(Dispatchers.IO) {
+            booksDao.getBooksFlow().collect {
+                when (results.size) {
+                    0 -> {
+                        results.add(it)
+                        barrier.arrive()
+                    }
+                    1 -> {
+                        results.add(it)
+                        barrier.arrive()
+                    }
+                    else -> fail("Should have only collected 2 results.")
+                }
+            }
+        }
+
+        barrier.arriveAndAwaitAdvance()
+        booksDao.insertBookSuspend(TestUtil.BOOK_3)
+
+        barrier.arriveAndAwaitAdvance()
+        assertThat(results.size).isEqualTo(2)
+        assertThat(results[0])
+            .isEqualTo(listOf(TestUtil.BOOK_1, TestUtil.BOOK_2))
+        assertThat(results[1])
+            .isEqualTo(listOf(TestUtil.BOOK_1, TestUtil.BOOK_2, TestUtil.BOOK_3))
+
+        job.cancelAndJoin()
+    }
+
+    @Test
+    fun receiveBooks() = runBlocking {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1, TestUtil.BOOK_2)
+
+        val channel = booksDao.getBooksFlow().produceIn(this)
+        assertThat(channel.receive())
+            .isEqualTo(listOf(TestUtil.BOOK_1, TestUtil.BOOK_2))
+        assertThat(channel.isEmpty).isTrue()
+
+        channel.cancel()
+    }
+
+    @Test
+    fun receiveBooks_update() = runBlocking {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1, TestUtil.BOOK_2)
+
+        val channel = booksDao.getBooksFlow().produceIn(this)
+
+        assertThat(channel.receive())
+            .isEqualTo(listOf(TestUtil.BOOK_1, TestUtil.BOOK_2))
+
+        booksDao.insertBookSuspend(TestUtil.BOOK_3)
+        drain() // drain async invalidate
+        yield()
+
+        assertThat(channel.receive())
+            .isEqualTo(listOf(TestUtil.BOOK_1, TestUtil.BOOK_2, TestUtil.BOOK_3))
+        assertThat(channel.isEmpty).isTrue()
+
+        channel.cancel()
+    }
+
+    @Test
+    fun receiveBooks_update_multipleChannels() = runBlocking {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1, TestUtil.BOOK_2)
+
+        val channels = Array(4) {
+            booksDao.getBooksFlow().produceIn(this)
+        }
+
+        channels.forEach {
+            assertThat(it.receive())
+                .isEqualTo(listOf(TestUtil.BOOK_1, TestUtil.BOOK_2))
+        }
+
+        booksDao.insertBookSuspend(TestUtil.BOOK_3)
+        drain() // drain async invalidate
+        yield()
+
+        channels.forEach {
+            assertThat(it.receive())
+                .isEqualTo(listOf(TestUtil.BOOK_1, TestUtil.BOOK_2, TestUtil.BOOK_3))
+            assertThat(it.isEmpty).isTrue()
+            it.cancel()
+        }
+    }
+
+    @Test
+    fun receiveBooks_update_multipleChannels_inTransaction() = runBlocking {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1, TestUtil.BOOK_2)
+
+        val channels = Array(4) {
+            booksDao.getBooksFlowInTransaction().produceIn(this)
+        }
+
+        channels.forEach {
+            assertThat(it.receive())
+                .isEqualTo(listOf(TestUtil.BOOK_1, TestUtil.BOOK_2))
+        }
+
+        booksDao.insertBookSuspend(TestUtil.BOOK_3)
+        drain() // drain async invalidate
+        yield()
+
+        channels.forEach {
+            assertThat(it.receive())
+                .isEqualTo(listOf(TestUtil.BOOK_1, TestUtil.BOOK_2, TestUtil.BOOK_3))
+            assertThat(it.isEmpty).isTrue()
+            it.cancel()
+        }
+    }
+
+    @Test
+    fun receiveBooks_latestUpdateOnly() = runBlocking {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1, TestUtil.BOOK_2)
+
+        val channel = booksDao.getBooksFlow().buffer(Channel.CONFLATED).produceIn(this)
+
+        assertThat(channel.receive())
+            .isEqualTo(listOf(TestUtil.BOOK_1, TestUtil.BOOK_2))
+
+        booksDao.insertBookSuspend(TestUtil.BOOK_3)
+        drain() // drain async invalidate
+        yield()
+        booksDao.deleteBookSuspend(TestUtil.BOOK_1)
+        drain() // drain async invalidate
+        yield()
+
+        assertThat(channel.receive())
+            .isEqualTo(listOf(TestUtil.BOOK_2, TestUtil.BOOK_3))
+        assertThat(channel.isEmpty).isTrue()
+
+        channel.cancel()
+    }
+
+    @Test
+    fun receiveBooks_async() = runBlocking {
+        booksDao.addAuthors(TestUtil.AUTHOR_1)
+        booksDao.addPublishers(TestUtil.PUBLISHER)
+        booksDao.addBooks(TestUtil.BOOK_1, TestUtil.BOOK_2)
+
+        val latch = CountDownLatch(1)
+        val job = async(Dispatchers.IO) {
+            for (result in booksDao.getBooksFlow().produceIn(this)) {
+                assertThat(result)
+                    .isEqualTo(listOf(TestUtil.BOOK_1, TestUtil.BOOK_2))
+                latch.countDown()
+            }
+        }
+
+        latch.await()
+        job.cancelAndJoin()
+    }
+}
\ No newline at end of file
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/TestDatabase.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/TestDatabase.java
index a6c6aba..f32c5cc 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/TestDatabase.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/TestDatabase.java
@@ -26,6 +26,7 @@
 import androidx.room.integration.testapp.dao.PetDao;
 import androidx.room.integration.testapp.dao.ProductDao;
 import androidx.room.integration.testapp.dao.RawDao;
+import androidx.room.integration.testapp.dao.RobotsDao;
 import androidx.room.integration.testapp.dao.SchoolDao;
 import androidx.room.integration.testapp.dao.SpecificDogDao;
 import androidx.room.integration.testapp.dao.ToyDao;
@@ -37,22 +38,26 @@
 import androidx.room.integration.testapp.vo.Day;
 import androidx.room.integration.testapp.vo.FriendsJunction;
 import androidx.room.integration.testapp.vo.FunnyNamedEntity;
+import androidx.room.integration.testapp.vo.Hivemind;
 import androidx.room.integration.testapp.vo.House;
 import androidx.room.integration.testapp.vo.Pet;
 import androidx.room.integration.testapp.vo.PetCouple;
 import androidx.room.integration.testapp.vo.PetWithUser;
 import androidx.room.integration.testapp.vo.Product;
+import androidx.room.integration.testapp.vo.Robot;
 import androidx.room.integration.testapp.vo.School;
 import androidx.room.integration.testapp.vo.Toy;
 import androidx.room.integration.testapp.vo.User;
 
+import java.nio.ByteBuffer;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.UUID;
 
 @Database(entities = {User.class, Pet.class, School.class, PetCouple.class, Toy.class,
         BlobEntity.class, Product.class, FunnyNamedEntity.class, House.class,
-        FriendsJunction.class},
+        FriendsJunction.class, Hivemind.class, Robot.class},
         views = {PetWithUser.class},
         version = 1, exportSchema = false)
 @TypeConverters(TestDatabase.Converters.class)
@@ -70,6 +75,7 @@
     public abstract FunnyNamedDao getFunnyNamedDao();
     public abstract RawDao getRawDao();
     public abstract UserHouseDao getUserHouseDao();
+    public abstract RobotsDao getRobotsDao();
 
     @SuppressWarnings("unused")
     public static class Converters {
@@ -106,5 +112,21 @@
             }
             return result;
         }
+
+        @TypeConverter
+        public UUID asUuid(byte[] bytes) {
+            ByteBuffer bb = ByteBuffer.wrap(bytes);
+            long firstLong = bb.getLong();
+            long secondLong = bb.getLong();
+            return new UUID(firstLong, secondLong);
+        }
+
+        @TypeConverter
+        public byte[] asBytes(UUID uuid) {
+            ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
+            bb.putLong(uuid.getMostSignificantBits());
+            bb.putLong(uuid.getLeastSignificantBits());
+            return bb.array();
+        }
     }
 }
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/RobotsDao.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/RobotsDao.java
new file mode 100644
index 0000000..ee4acd8
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/RobotsDao.java
@@ -0,0 +1,43 @@
+/*
+ * 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.room.integration.testapp.dao;
+
+import androidx.room.Dao;
+import androidx.room.Insert;
+import androidx.room.Query;
+import androidx.room.integration.testapp.vo.Cluster;
+import androidx.room.integration.testapp.vo.Hivemind;
+import androidx.room.integration.testapp.vo.Robot;
+
+import java.util.List;
+import java.util.UUID;
+
+@Dao
+public interface RobotsDao {
+
+    @Insert
+    void putHivemind(Hivemind hivemind);
+
+    @Insert
+    void putRobot(Robot robot);
+
+    @Query("SELECT * FROM Hivemind")
+    List<Cluster> getCluster();
+
+    @Query("SELECT * FROM Robot WHERE mHiveId = :hiveId")
+    List<Robot> getHiveRobots(UUID hiveId);
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/UserDao.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/UserDao.java
index c489c27..449f2b5 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/UserDao.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/UserDao.java
@@ -33,6 +33,7 @@
 import androidx.room.integration.testapp.vo.Day;
 import androidx.room.integration.testapp.vo.IdUsername;
 import androidx.room.integration.testapp.vo.NameAndLastName;
+import androidx.room.integration.testapp.vo.NameAndUsers;
 import androidx.room.integration.testapp.vo.User;
 import androidx.room.integration.testapp.vo.UserAndFriends;
 import androidx.room.integration.testapp.vo.UserSummary;
@@ -318,4 +319,7 @@
 
     @Query("UPDATE user SET mName = :name, mLastName = :name WHERE mId = :userId")
     public abstract void setSameNames(String name, int userId);
+
+    @Query("SELECT mName FROM User")
+    public abstract List<NameAndUsers> getNameAndUsers();
 }
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/PojoWithRelationTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/PojoWithRelationTest.java
index 18540a3..1cced11 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/PojoWithRelationTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/PojoWithRelationTest.java
@@ -20,11 +20,14 @@
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+import androidx.room.integration.testapp.vo.Cluster;
 import androidx.room.integration.testapp.vo.EmbeddedUserAndAllPets;
+import androidx.room.integration.testapp.vo.Hivemind;
 import androidx.room.integration.testapp.vo.House;
 import androidx.room.integration.testapp.vo.Pet;
 import androidx.room.integration.testapp.vo.PetAndOwner;
 import androidx.room.integration.testapp.vo.PetWithToyIds;
+import androidx.room.integration.testapp.vo.Robot;
 import androidx.room.integration.testapp.vo.Toy;
 import androidx.room.integration.testapp.vo.User;
 import androidx.room.integration.testapp.vo.UserAndAllPets;
@@ -44,6 +47,7 @@
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
+import java.util.UUID;
 
 @RunWith(AndroidJUnit4.class)
 @LargeTest
@@ -295,4 +299,36 @@
         assertThat(petAndOwners.get(1).getUser().getId(), is(1));
         assertThat(petAndOwners.get(2).getUser().getId(), is(2));
     }
+
+    @Test
+    public void relationWithBlobKey() {
+        UUID hiveId1 = UUID.randomUUID();
+        UUID hiveId2 = UUID.randomUUID();
+        UUID robotId1 = UUID.randomUUID();
+        UUID robotId2 = UUID.randomUUID();
+        UUID robotId3 = UUID.randomUUID();
+
+        mRobotsDao.putHivemind(new Hivemind(hiveId1));
+        mRobotsDao.putHivemind(new Hivemind(hiveId2));
+        mRobotsDao.putRobot(new Robot(robotId1, hiveId1));
+        mRobotsDao.putRobot(new Robot(robotId2, hiveId1));
+        mRobotsDao.putRobot(new Robot(robotId3, hiveId2));
+
+        List<Robot> firstHiveRobots = mRobotsDao.getHiveRobots(hiveId1);
+        assertThat(firstHiveRobots.size(), is(2));
+        assertThat(firstHiveRobots.get(0).mId, is(robotId1));
+        assertThat(firstHiveRobots.get(1).mId, is(robotId2));
+
+        List<Robot> secondHiveRobots = mRobotsDao.getHiveRobots(hiveId2);
+        assertThat(secondHiveRobots.size(), is(1));
+        assertThat(secondHiveRobots.get(0).mId, is(robotId3));
+
+        List<Cluster> clusterResult = mRobotsDao.getCluster();
+        assertThat(clusterResult.size(), is(2));
+        assertThat(clusterResult.get(0).mRobotList.size(), is(2));
+        assertThat(clusterResult.get(0).mRobotList.get(0).mId, is(robotId1));
+        assertThat(clusterResult.get(0).mRobotList.get(1).mId, is(robotId2));
+        assertThat(clusterResult.get(1).mRobotList.size(), is(1));
+        assertThat(clusterResult.get(1).mRobotList.get(0).mId, is(robotId3));
+    }
 }
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/TestDatabaseTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/TestDatabaseTest.java
index bc535c6..6f6e8b8 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/TestDatabaseTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/TestDatabaseTest.java
@@ -24,6 +24,7 @@
 import androidx.room.integration.testapp.dao.PetCoupleDao;
 import androidx.room.integration.testapp.dao.PetDao;
 import androidx.room.integration.testapp.dao.RawDao;
+import androidx.room.integration.testapp.dao.RobotsDao;
 import androidx.room.integration.testapp.dao.SchoolDao;
 import androidx.room.integration.testapp.dao.SpecificDogDao;
 import androidx.room.integration.testapp.dao.ToyDao;
@@ -49,6 +50,7 @@
     protected FunnyNamedDao mFunnyNamedDao;
     protected RawDao mRawDao;
     protected UserHouseDao mUserHouseDao;
+    protected RobotsDao mRobotsDao;
 
     @Before
     public void createDb() {
@@ -65,5 +67,6 @@
         mFunnyNamedDao = mDatabase.getFunnyNamedDao();
         mRawDao = mDatabase.getRawDao();
         mUserHouseDao = mDatabase.getUserHouseDao();
+        mRobotsDao = mDatabase.getRobotsDao();
     }
 }
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/Cluster.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/Cluster.java
new file mode 100644
index 0000000..b2c55a2
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/Cluster.java
@@ -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.
+ */
+
+package androidx.room.integration.testapp.vo;
+
+import androidx.room.Embedded;
+import androidx.room.Relation;
+
+import java.util.List;
+
+public class Cluster {
+
+    @Embedded
+    public final Hivemind mHivemind;
+
+    @Relation(parentColumn = "mId", entity = Robot.class, entityColumn = "mHiveId")
+    public final List<Robot> mRobotList;
+
+    public Cluster(Hivemind hivemind, List<Robot> robotList) {
+        mHivemind = hivemind;
+        mRobotList = robotList;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/Hivemind.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/Hivemind.java
new file mode 100644
index 0000000..935ee51
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/Hivemind.java
@@ -0,0 +1,35 @@
+/*
+ * 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.room.integration.testapp.vo;
+
+import androidx.annotation.NonNull;
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+import java.util.UUID;
+
+@Entity
+public class Hivemind {
+
+    @PrimaryKey
+    @NonNull
+    public final UUID mId;
+
+    public Hivemind(@NonNull UUID id) {
+        mId = id;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/NameAndUsers.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/NameAndUsers.java
new file mode 100644
index 0000000..a1b7f26
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/NameAndUsers.java
@@ -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.room.integration.testapp.vo;
+
+import androidx.room.Relation;
+
+import java.util.List;
+
+public class NameAndUsers {
+    String mName;
+    @Relation(parentColumn = "mName", entityColumn = "mName")
+    List<User> mUsers;
+
+    public NameAndUsers(String name, List<User> users) {
+        mName = name;
+        mUsers = users;
+    }
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/Robot.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/Robot.java
new file mode 100644
index 0000000..0d7be3a
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/vo/Robot.java
@@ -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.
+ */
+
+package androidx.room.integration.testapp.vo;
+
+import androidx.annotation.NonNull;
+import androidx.room.Entity;
+import androidx.room.PrimaryKey;
+
+import java.util.UUID;
+
+@Entity
+public class Robot {
+
+    @PrimaryKey
+    @NonNull
+    public final UUID mId;
+    @NonNull
+    public final UUID mHiveId;
+
+    public Robot(@NonNull UUID id, @NonNull UUID hiveId) {
+        mId = id;
+        mHiveId = hiveId;
+    }
+}
diff --git a/room/ktx/api/restricted_2.2.0-alpha01.txt b/room/ktx/api/restricted_2.2.0-alpha01.txt
index dc4a42bf..20837edc 100644
--- a/room/ktx/api/restricted_2.2.0-alpha01.txt
+++ b/room/ktx/api/restricted_2.2.0-alpha01.txt
@@ -2,11 +2,13 @@
 package androidx.room {
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class CoroutinesRoom {
+    method public static <R> kotlinx.coroutines.flow.Flow<R> createFlow(androidx.room.RoomDatabase db, boolean inTransaction, String![] tableNames, java.util.concurrent.Callable<R> callable);
     method public static suspend <R> Object! execute(androidx.room.RoomDatabase p, boolean db, java.util.concurrent.Callable<R> inTransaction, kotlin.coroutines.Continuation<? super R> callable);
     field public static final androidx.room.CoroutinesRoom.Companion! Companion;
   }
 
   public static final class CoroutinesRoom.Companion {
+    method public <R> kotlinx.coroutines.flow.Flow<R> createFlow(androidx.room.RoomDatabase db, boolean inTransaction, String![] tableNames, java.util.concurrent.Callable<R> callable);
     method public suspend <R> Object! execute(androidx.room.RoomDatabase db, boolean inTransaction, java.util.concurrent.Callable<R> callable, kotlin.coroutines.Continuation<? super R> p);
   }
 
diff --git a/room/ktx/api/restricted_2.2.0-alpha02.txt b/room/ktx/api/restricted_2.2.0-alpha02.txt
index dc4a42bf..20837edc 100644
--- a/room/ktx/api/restricted_2.2.0-alpha02.txt
+++ b/room/ktx/api/restricted_2.2.0-alpha02.txt
@@ -2,11 +2,13 @@
 package androidx.room {
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class CoroutinesRoom {
+    method public static <R> kotlinx.coroutines.flow.Flow<R> createFlow(androidx.room.RoomDatabase db, boolean inTransaction, String![] tableNames, java.util.concurrent.Callable<R> callable);
     method public static suspend <R> Object! execute(androidx.room.RoomDatabase p, boolean db, java.util.concurrent.Callable<R> inTransaction, kotlin.coroutines.Continuation<? super R> callable);
     field public static final androidx.room.CoroutinesRoom.Companion! Companion;
   }
 
   public static final class CoroutinesRoom.Companion {
+    method public <R> kotlinx.coroutines.flow.Flow<R> createFlow(androidx.room.RoomDatabase db, boolean inTransaction, String![] tableNames, java.util.concurrent.Callable<R> callable);
     method public suspend <R> Object! execute(androidx.room.RoomDatabase db, boolean inTransaction, java.util.concurrent.Callable<R> callable, kotlin.coroutines.Continuation<? super R> p);
   }
 
diff --git a/room/ktx/api/restricted_current.txt b/room/ktx/api/restricted_current.txt
index dc4a42bf..20837edc 100644
--- a/room/ktx/api/restricted_current.txt
+++ b/room/ktx/api/restricted_current.txt
@@ -2,11 +2,13 @@
 package androidx.room {
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class CoroutinesRoom {
+    method public static <R> kotlinx.coroutines.flow.Flow<R> createFlow(androidx.room.RoomDatabase db, boolean inTransaction, String![] tableNames, java.util.concurrent.Callable<R> callable);
     method public static suspend <R> Object! execute(androidx.room.RoomDatabase p, boolean db, java.util.concurrent.Callable<R> inTransaction, kotlin.coroutines.Continuation<? super R> callable);
     field public static final androidx.room.CoroutinesRoom.Companion! Companion;
   }
 
   public static final class CoroutinesRoom.Companion {
+    method public <R> kotlinx.coroutines.flow.Flow<R> createFlow(androidx.room.RoomDatabase db, boolean inTransaction, String![] tableNames, java.util.concurrent.Callable<R> callable);
     method public suspend <R> Object! execute(androidx.room.RoomDatabase db, boolean inTransaction, java.util.concurrent.Callable<R> callable, kotlin.coroutines.Continuation<? super R> p);
   }
 
diff --git a/room/ktx/build.gradle b/room/ktx/build.gradle
index 3a13204..6615260 100644
--- a/room/ktx/build.gradle
+++ b/room/ktx/build.gradle
@@ -30,9 +30,11 @@
     api(project(":room:room-common"))
     api(project(":room:room-runtime"))
     api(KOTLIN_STDLIB)
-    api(KOTLIN_COROUTINES)
+    api(KOTLIN_COROUTINES_PREVIEW)
     testImplementation(JUNIT)
     testImplementation(MOCKITO_CORE)
+    testImplementation(TRUTH)
+    testImplementation(ARCH_LIFECYCLE_LIVEDATA_CORE)
 }
 
 androidx {
diff --git a/room/ktx/src/main/java/androidx/room/CoroutinesRoom.kt b/room/ktx/src/main/java/androidx/room/CoroutinesRoom.kt
index d38ed58..c7befed 100644
--- a/room/ktx/src/main/java/androidx/room/CoroutinesRoom.kt
+++ b/room/ktx/src/main/java/androidx/room/CoroutinesRoom.kt
@@ -19,6 +19,9 @@
 import androidx.annotation.RestrictTo
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.withContext
 import java.util.concurrent.Callable
 import kotlin.coroutines.coroutineContext
@@ -51,6 +54,38 @@
                 callable.call()
             }
         }
+
+        @JvmStatic
+        fun <R> createFlow(
+            db: RoomDatabase,
+            inTransaction: Boolean,
+            tableNames: Array<String>,
+            callable: Callable<R>
+        ): Flow<@JvmSuppressWildcards R> = flow {
+            // Observer channel receives signals from the invalidation tracker to emit queries.
+            val observerChannel = Channel<Unit>(Channel.CONFLATED)
+            val observer = object : InvalidationTracker.Observer(tableNames) {
+                override fun onInvalidated(tables: MutableSet<String>) {
+                    observerChannel.offer(Unit)
+                }
+            }
+            observerChannel.offer(Unit) // Initial signal to perform first query.
+            val flowContext = coroutineContext
+            val queryContext = if (inTransaction) db.transactionDispatcher else db.queryDispatcher
+            withContext(queryContext) {
+                db.invalidationTracker.addObserver(observer)
+                try {
+                    // Iterate until cancelled, transforming observer signals to query results to
+                    // be emitted to the flow.
+                    for (signal in observerChannel) {
+                        val result = callable.call()
+                        withContext(flowContext) { emit(result) }
+                    }
+                } finally {
+                    db.invalidationTracker.removeObserver(observer)
+                }
+            }
+        }
     }
 }
 
@@ -71,5 +106,5 @@
  */
 internal val RoomDatabase.transactionDispatcher: CoroutineDispatcher
     get() = backingFieldMap.getOrPut("TransactionDispatcher") {
-        queryExecutor.asCoroutineDispatcher()
+        transactionExecutor.asCoroutineDispatcher()
     } as CoroutineDispatcher
diff --git a/room/ktx/src/test/java/androidx/room/CoroutinesRoomTest.kt b/room/ktx/src/test/java/androidx/room/CoroutinesRoomTest.kt
new file mode 100644
index 0000000..ad1841f
--- /dev/null
+++ b/room/ktx/src/test/java/androidx/room/CoroutinesRoomTest.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.room
+
+import androidx.sqlite.db.SupportSQLiteOpenHelper
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.async
+import kotlinx.coroutines.cancelAndJoin
+import kotlinx.coroutines.flow.single
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import java.util.concurrent.Callable
+import kotlin.coroutines.ContinuationInterceptor
+
+@FlowPreview
+@RunWith(JUnit4::class)
+class CoroutinesRoomTest {
+
+    private val database = TestDatabase()
+    private val invalidationTracker = database.invalidationTracker as TestInvalidationTracker
+
+    @Test
+    fun testCreateFlow() = testRun {
+        var callableExecuted = false
+        val flow = CoroutinesRoom.createFlow(
+            db = database,
+            inTransaction = false,
+            tableNames = arrayOf("Pet"),
+            callable = Callable { callableExecuted = true }
+        )
+
+        assertThat(invalidationTracker.observers.isEmpty()).isTrue()
+        assertThat(callableExecuted).isFalse()
+
+        val job = async {
+            flow.single()
+        }
+        yield(); yield() // yield for async and flow
+
+        assertThat(invalidationTracker.observers.size).isEqualTo(1)
+        assertThat(callableExecuted).isTrue()
+
+        job.cancelAndJoin()
+        assertThat(invalidationTracker.observers.isEmpty()).isTrue()
+    }
+
+    // Use runBlocking dispatcher as query dispatchers, keeps the tests consistent.
+    private fun testRun(block: suspend CoroutineScope.() -> Unit) = runBlocking {
+        database.backingFieldMap["QueryDispatcher"] = coroutineContext[ContinuationInterceptor]
+        block.invoke(this)
+    }
+
+    private class TestDatabase : RoomDatabase() {
+        override fun createOpenHelper(config: DatabaseConfiguration?): SupportSQLiteOpenHelper {
+            throw UnsupportedOperationException("Shouldn't be called!")
+        }
+
+        override fun createInvalidationTracker(): InvalidationTracker {
+            return TestInvalidationTracker(this)
+        }
+
+        override fun clearAllTables() {
+            throw UnsupportedOperationException("Shouldn't be called!")
+        }
+    }
+
+    private class TestInvalidationTracker(db: RoomDatabase) : InvalidationTracker(db) {
+        val observers = mutableListOf<Observer>()
+
+        override fun addObserver(observer: Observer) {
+            observers.add(observer)
+        }
+
+        override fun removeObserver(observer: Observer) {
+            observers.remove(observer)
+        }
+    }
+}
diff --git a/room/runtime/api/2.1.0-beta02.txt b/room/runtime/api/2.1.0-beta02.txt
deleted file mode 100644
index 687fdf9..0000000
--- a/room/runtime/api/2.1.0-beta02.txt
+++ /dev/null
@@ -1,111 +0,0 @@
-// Signature format: 3.0
-package androidx.room {
-
-  public class DatabaseConfiguration {
-    method public boolean isMigrationRequired(int, int);
-    method @Deprecated public boolean isMigrationRequiredFrom(int);
-    field public final boolean allowDestructiveMigrationOnDowngrade;
-    field public final boolean allowMainThreadQueries;
-    field public final java.util.List<androidx.room.RoomDatabase.Callback!>? callbacks;
-    field public final android.content.Context context;
-    field public final androidx.room.RoomDatabase.JournalMode! journalMode;
-    field public final androidx.room.RoomDatabase.MigrationContainer migrationContainer;
-    field public final boolean multiInstanceInvalidation;
-    field public final String? name;
-    field public final java.util.concurrent.Executor queryExecutor;
-    field public final boolean requireMigration;
-    field public final androidx.sqlite.db.SupportSQLiteOpenHelper.Factory sqliteOpenHelperFactory;
-    field public final java.util.concurrent.Executor transactionExecutor;
-  }
-
-  public class InvalidationTracker {
-    method @WorkerThread public void addObserver(androidx.room.InvalidationTracker.Observer);
-    method public void refreshVersionsAsync();
-    method @WorkerThread public void removeObserver(androidx.room.InvalidationTracker.Observer);
-  }
-
-  public abstract static class InvalidationTracker.Observer {
-    ctor protected InvalidationTracker.Observer(String, java.lang.String!...);
-    ctor public InvalidationTracker.Observer(String![]);
-    method public abstract void onInvalidated(java.util.Set<java.lang.String!>);
-  }
-
-  public class Room {
-    ctor @Deprecated public Room();
-    method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T!> databaseBuilder(android.content.Context, Class<T!>, String);
-    method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T!> inMemoryDatabaseBuilder(android.content.Context, Class<T!>);
-    field public static final String MASTER_TABLE_NAME = "room_master_table";
-  }
-
-  public abstract class RoomDatabase {
-    ctor public RoomDatabase();
-    method @Deprecated public void beginTransaction();
-    method @WorkerThread public abstract void clearAllTables();
-    method public void close();
-    method public androidx.sqlite.db.SupportSQLiteStatement! compileStatement(String);
-    method protected abstract androidx.room.InvalidationTracker createInvalidationTracker();
-    method protected abstract androidx.sqlite.db.SupportSQLiteOpenHelper createOpenHelper(androidx.room.DatabaseConfiguration!);
-    method @Deprecated public void endTransaction();
-    method public androidx.room.InvalidationTracker getInvalidationTracker();
-    method public androidx.sqlite.db.SupportSQLiteOpenHelper getOpenHelper();
-    method public java.util.concurrent.Executor getQueryExecutor();
-    method public java.util.concurrent.Executor getTransactionExecutor();
-    method public boolean inTransaction();
-    method @CallSuper public void init(androidx.room.DatabaseConfiguration);
-    method protected void internalInitInvalidationTracker(androidx.sqlite.db.SupportSQLiteDatabase);
-    method public boolean isOpen();
-    method public android.database.Cursor! query(String!, Object![]?);
-    method public android.database.Cursor! query(androidx.sqlite.db.SupportSQLiteQuery!);
-    method public void runInTransaction(Runnable);
-    method public <V> V! runInTransaction(java.util.concurrent.Callable<V!>);
-    method @Deprecated public void setTransactionSuccessful();
-    field @Deprecated protected java.util.List<androidx.room.RoomDatabase.Callback!>? mCallbacks;
-    field @Deprecated protected volatile androidx.sqlite.db.SupportSQLiteDatabase! mDatabase;
-  }
-
-  public static class RoomDatabase.Builder<T extends androidx.room.RoomDatabase> {
-    method public androidx.room.RoomDatabase.Builder<T!> addCallback(androidx.room.RoomDatabase.Callback);
-    method public androidx.room.RoomDatabase.Builder<T!> addMigrations(androidx.room.migration.Migration!...);
-    method public androidx.room.RoomDatabase.Builder<T!> allowMainThreadQueries();
-    method public T build();
-    method public androidx.room.RoomDatabase.Builder<T!> enableMultiInstanceInvalidation();
-    method public androidx.room.RoomDatabase.Builder<T!> fallbackToDestructiveMigration();
-    method public androidx.room.RoomDatabase.Builder<T!> fallbackToDestructiveMigrationFrom(int...);
-    method public androidx.room.RoomDatabase.Builder<T!> fallbackToDestructiveMigrationOnDowngrade();
-    method public androidx.room.RoomDatabase.Builder<T!> openHelperFactory(androidx.sqlite.db.SupportSQLiteOpenHelper.Factory?);
-    method public androidx.room.RoomDatabase.Builder<T!> setJournalMode(androidx.room.RoomDatabase.JournalMode);
-    method public androidx.room.RoomDatabase.Builder<T!> setQueryExecutor(java.util.concurrent.Executor);
-    method public androidx.room.RoomDatabase.Builder<T!> setTransactionExecutor(java.util.concurrent.Executor);
-  }
-
-  public abstract static class RoomDatabase.Callback {
-    ctor public RoomDatabase.Callback();
-    method public void onCreate(androidx.sqlite.db.SupportSQLiteDatabase);
-    method public void onOpen(androidx.sqlite.db.SupportSQLiteDatabase);
-  }
-
-  public enum RoomDatabase.JournalMode {
-    enum_constant public static final androidx.room.RoomDatabase.JournalMode AUTOMATIC;
-    enum_constant public static final androidx.room.RoomDatabase.JournalMode TRUNCATE;
-    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.JELLY_BEAN) public static final androidx.room.RoomDatabase.JournalMode WRITE_AHEAD_LOGGING;
-  }
-
-  public static class RoomDatabase.MigrationContainer {
-    ctor public RoomDatabase.MigrationContainer();
-    method public void addMigrations(androidx.room.migration.Migration!...);
-    method public java.util.List<androidx.room.migration.Migration!>? findMigrationPath(int, int);
-  }
-
-}
-
-package androidx.room.migration {
-
-  public abstract class Migration {
-    ctor public Migration(int, int);
-    method public abstract void migrate(androidx.sqlite.db.SupportSQLiteDatabase);
-    field public final int endVersion;
-    field public final int startVersion;
-  }
-
-}
-
diff --git a/room/runtime/api/res-2.1.0-beta02.txt b/room/runtime/api/res-2.1.0-beta02.txt
deleted file mode 100644
index e69de29..0000000
--- a/room/runtime/api/res-2.1.0-beta02.txt
+++ /dev/null
diff --git a/room/runtime/api/restricted_2.1.0-beta02.txt b/room/runtime/api/restricted_2.1.0-beta02.txt
deleted file mode 100644
index 8ed4107..0000000
--- a/room/runtime/api/restricted_2.1.0-beta02.txt
+++ /dev/null
@@ -1,185 +0,0 @@
-// Signature format: 3.0
-package androidx.room {
-
-  public class DatabaseConfiguration {
-    ctor @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public DatabaseConfiguration(android.content.Context, String?, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory, androidx.room.RoomDatabase.MigrationContainer, java.util.List<androidx.room.RoomDatabase.Callback!>?, boolean, androidx.room.RoomDatabase.JournalMode!, java.util.concurrent.Executor, boolean, java.util.Set<java.lang.Integer!>?);
-    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public DatabaseConfiguration(android.content.Context, String?, androidx.sqlite.db.SupportSQLiteOpenHelper.Factory, androidx.room.RoomDatabase.MigrationContainer, java.util.List<androidx.room.RoomDatabase.Callback!>?, boolean, androidx.room.RoomDatabase.JournalMode!, java.util.concurrent.Executor, java.util.concurrent.Executor, boolean, boolean, boolean, java.util.Set<java.lang.Integer!>?);
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class EntityDeletionOrUpdateAdapter<T> extends androidx.room.SharedSQLiteStatement {
-    ctor public EntityDeletionOrUpdateAdapter(androidx.room.RoomDatabase!);
-    method protected abstract void bind(androidx.sqlite.db.SupportSQLiteStatement!, T!);
-    method public final int handle(T!);
-    method public final int handleMultiple(Iterable<T!>!);
-    method public final int handleMultiple(T![]!);
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class EntityInsertionAdapter<T> extends androidx.room.SharedSQLiteStatement {
-    ctor public EntityInsertionAdapter(androidx.room.RoomDatabase!);
-    method protected abstract void bind(androidx.sqlite.db.SupportSQLiteStatement!, T!);
-    method public final void insert(T!);
-    method public final void insert(T![]!);
-    method public final void insert(Iterable<T!>!);
-    method public final long insertAndReturnId(T!);
-    method public final long[]! insertAndReturnIdsArray(java.util.Collection<T!>!);
-    method public final long[]! insertAndReturnIdsArray(T![]!);
-    method public final Long![]! insertAndReturnIdsArrayBox(java.util.Collection<T!>!);
-    method public final Long![]! insertAndReturnIdsArrayBox(T![]!);
-    method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(T![]!);
-    method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(java.util.Collection<T!>!);
-  }
-
-  public class InvalidationTracker {
-    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public InvalidationTracker(androidx.room.RoomDatabase!, java.lang.String!...);
-    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public InvalidationTracker(androidx.room.RoomDatabase!, java.util.Map<java.lang.String!,java.lang.String!>!, java.util.Map<java.lang.String!,java.util.Set<java.lang.String!>!>!, java.lang.String!...);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void addWeakObserver(androidx.room.InvalidationTracker.Observer!);
-    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> androidx.lifecycle.LiveData<T!>! createLiveData(String![]!, java.util.concurrent.Callable<T!>!);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> androidx.lifecycle.LiveData<T!>! createLiveData(String![]!, boolean, java.util.concurrent.Callable<T!>!);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @WorkerThread public void refreshVersionsSync();
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class MultiInstanceInvalidationService extends android.app.Service {
-    ctor public MultiInstanceInvalidationService();
-    method public android.os.IBinder? onBind(android.content.Intent!);
-  }
-
-  public abstract class RoomDatabase {
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void assertNotMainThread();
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public void assertNotSuspendingTransaction();
-    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final int MAX_BIND_PARAMETER_CNT = 999; // 0x3e7
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class RoomOpenHelper extends androidx.sqlite.db.SupportSQLiteOpenHelper.Callback {
-    ctor public RoomOpenHelper(androidx.room.DatabaseConfiguration, androidx.room.RoomOpenHelper.Delegate, String, String);
-    ctor public RoomOpenHelper(androidx.room.DatabaseConfiguration, androidx.room.RoomOpenHelper.Delegate, String);
-    method public void onCreate(androidx.sqlite.db.SupportSQLiteDatabase!);
-    method public void onUpgrade(androidx.sqlite.db.SupportSQLiteDatabase!, int, int);
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract static class RoomOpenHelper.Delegate {
-    ctor public RoomOpenHelper.Delegate(int);
-    method protected abstract void createAllTables(androidx.sqlite.db.SupportSQLiteDatabase!);
-    method protected abstract void dropAllTables(androidx.sqlite.db.SupportSQLiteDatabase!);
-    method protected abstract void onCreate(androidx.sqlite.db.SupportSQLiteDatabase!);
-    method protected abstract void onOpen(androidx.sqlite.db.SupportSQLiteDatabase!);
-    method protected void onPostMigrate(androidx.sqlite.db.SupportSQLiteDatabase!);
-    method protected void onPreMigrate(androidx.sqlite.db.SupportSQLiteDatabase!);
-    method protected abstract void validateMigration(androidx.sqlite.db.SupportSQLiteDatabase!);
-    field public final int version;
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class RoomSQLiteQuery implements androidx.sqlite.db.SupportSQLiteProgram androidx.sqlite.db.SupportSQLiteQuery {
-    method public static androidx.room.RoomSQLiteQuery! acquire(String!, int);
-    method public void bindBlob(int, byte[]!);
-    method public void bindDouble(int, double);
-    method public void bindLong(int, long);
-    method public void bindNull(int);
-    method public void bindString(int, String!);
-    method public void bindTo(androidx.sqlite.db.SupportSQLiteProgram!);
-    method public void clearBindings();
-    method public void close();
-    method public void copyArgumentsFrom(androidx.room.RoomSQLiteQuery!);
-    method public static androidx.room.RoomSQLiteQuery! copyFrom(androidx.sqlite.db.SupportSQLiteQuery!);
-    method public int getArgCount();
-    method public String! getSql();
-    method public void release();
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class SharedSQLiteStatement {
-    ctor public SharedSQLiteStatement(androidx.room.RoomDatabase!);
-    method public androidx.sqlite.db.SupportSQLiteStatement! acquire();
-    method protected void assertNotMainThread();
-    method protected abstract String! createQuery();
-    method public void release(androidx.sqlite.db.SupportSQLiteStatement!);
-  }
-
-}
-
-package androidx.room.paging {
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class LimitOffsetDataSource<T> extends androidx.paging.PositionalDataSource<T> {
-    ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase!, androidx.sqlite.db.SupportSQLiteQuery!, boolean, java.lang.String!...);
-    ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase!, androidx.room.RoomSQLiteQuery!, boolean, java.lang.String!...);
-    method protected abstract java.util.List<T!>! convertRows(android.database.Cursor!);
-    method public void loadInitial(androidx.paging.PositionalDataSource.LoadInitialParams, androidx.paging.PositionalDataSource.LoadInitialCallback<T!>);
-    method public void loadRange(androidx.paging.PositionalDataSource.LoadRangeParams, androidx.paging.PositionalDataSource.LoadRangeCallback<T!>);
-  }
-
-}
-
-package androidx.room.util {
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class CursorUtil {
-    method public static android.database.Cursor copyAndClose(android.database.Cursor);
-    method public static int getColumnIndex(android.database.Cursor, String);
-    method public static int getColumnIndexOrThrow(android.database.Cursor, String);
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class DBUtil {
-    method public static void dropFtsSyncTriggers(androidx.sqlite.db.SupportSQLiteDatabase!);
-    method public static android.database.Cursor query(androidx.room.RoomDatabase!, androidx.sqlite.db.SupportSQLiteQuery!, boolean);
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class FtsTableInfo {
-    ctor public FtsTableInfo(String!, java.util.Set<java.lang.String!>!, java.util.Set<java.lang.String!>!);
-    ctor public FtsTableInfo(String!, java.util.Set<java.lang.String!>!, String!);
-    method public static androidx.room.util.FtsTableInfo! read(androidx.sqlite.db.SupportSQLiteDatabase!, String!);
-    field public final java.util.Set<java.lang.String!>! columns;
-    field public final String! name;
-    field public final java.util.Set<java.lang.String!>! options;
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class StringUtil {
-    method public static void appendPlaceholders(StringBuilder!, int);
-    method public static String? joinIntoString(java.util.List<java.lang.Integer!>?);
-    method public static StringBuilder! newStringBuilder();
-    method public static java.util.List<java.lang.Integer!>? splitToIntList(String?);
-    field public static final String![]! EMPTY_STRING_ARRAY;
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TableInfo {
-    ctor public TableInfo(String!, java.util.Map<java.lang.String!,androidx.room.util.TableInfo.Column!>!, java.util.Set<androidx.room.util.TableInfo.ForeignKey!>!, java.util.Set<androidx.room.util.TableInfo.Index!>!);
-    ctor public TableInfo(String!, java.util.Map<java.lang.String!,androidx.room.util.TableInfo.Column!>!, java.util.Set<androidx.room.util.TableInfo.ForeignKey!>!);
-    method public static androidx.room.util.TableInfo! read(androidx.sqlite.db.SupportSQLiteDatabase!, String!);
-    field public final java.util.Map<java.lang.String!,androidx.room.util.TableInfo.Column!>! columns;
-    field public final java.util.Set<androidx.room.util.TableInfo.ForeignKey!>! foreignKeys;
-    field public final java.util.Set<androidx.room.util.TableInfo.Index!>? indices;
-    field public final String! name;
-  }
-
-  public static class TableInfo.Column {
-    ctor public TableInfo.Column(String!, String!, boolean, int);
-    method public boolean isPrimaryKey();
-    field public final int affinity;
-    field public final String! name;
-    field public final boolean notNull;
-    field public final int primaryKeyPosition;
-    field public final String! type;
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class TableInfo.ForeignKey {
-    ctor public TableInfo.ForeignKey(String, String, String, java.util.List<java.lang.String!>, java.util.List<java.lang.String!>);
-    field public final java.util.List<java.lang.String!> columnNames;
-    field public final String onDelete;
-    field public final String onUpdate;
-    field public final java.util.List<java.lang.String!> referenceColumnNames;
-    field public final String referenceTable;
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class TableInfo.Index {
-    ctor public TableInfo.Index(String!, boolean, java.util.List<java.lang.String!>!);
-    field public static final String DEFAULT_PREFIX = "index_";
-    field public final java.util.List<java.lang.String!>! columns;
-    field public final String! name;
-    field public final boolean unique;
-  }
-
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ViewInfo {
-    ctor public ViewInfo(String!, String!);
-    method public static androidx.room.util.ViewInfo! read(androidx.sqlite.db.SupportSQLiteDatabase!, String!);
-    field public final String! name;
-    field public final String! sql;
-  }
-
-}
-
diff --git a/room/runtime/api/restricted_2.2.0-alpha02.ignore b/room/runtime/api/restricted_2.2.0-alpha02.ignore
index a1d60ff..6984c60 100644
--- a/room/runtime/api/restricted_2.2.0-alpha02.ignore
+++ b/room/runtime/api/restricted_2.2.0-alpha02.ignore
@@ -1,7 +1,19 @@
 // Baseline format: 1.0
-AddedAbstractMethod: androidx.room.RoomDatabase#clearAllTables():
-    Added method androidx.room.RoomDatabase.clearAllTables()
-AddedAbstractMethod: androidx.room.RoomDatabase#createInvalidationTracker():
-    Added method androidx.room.RoomDatabase.createInvalidationTracker()
-AddedAbstractMethod: androidx.room.RoomDatabase#createOpenHelper(androidx.room.DatabaseConfiguration):
-    Added method androidx.room.RoomDatabase.createOpenHelper(androidx.room.DatabaseConfiguration)
+ChangedType: androidx.room.InvalidationTracker#createLiveData(String[], boolean, java.util.concurrent.Callable<T>):
+    Method androidx.room.InvalidationTracker.createLiveData has changed return type from LiveData<T> to androidx.lifecycle.LiveData<T>
+ChangedType: androidx.room.InvalidationTracker#createLiveData(String[], java.util.concurrent.Callable<T>):
+    Method androidx.room.InvalidationTracker.createLiveData has changed return type from LiveData<T> to androidx.lifecycle.LiveData<T>
+
+
+InvalidNullConversion: androidx.room.InvalidationTracker#createLiveData(String[], boolean, java.util.concurrent.Callable<T>):
+    Attempted to remove @NonNull annotation from method androidx.room.InvalidationTracker.createLiveData(String[],boolean,java.util.concurrent.Callable<T>)
+InvalidNullConversion: androidx.room.InvalidationTracker#createLiveData(String[], java.util.concurrent.Callable<T>):
+    Attempted to remove @NonNull annotation from method androidx.room.InvalidationTracker.createLiveData(String[],java.util.concurrent.Callable<T>)
+
+
+RemovedMethod: androidx.room.paging.LimitOffsetDataSource#isInvalid():
+    Removed method androidx.room.paging.LimitOffsetDataSource.isInvalid()
+RemovedMethod: androidx.room.paging.LimitOffsetDataSource#loadInitial(LoadInitialParams, LoadInitialCallback<T>):
+    Removed method androidx.room.paging.LimitOffsetDataSource.loadInitial(LoadInitialParams,LoadInitialCallback<T>)
+RemovedMethod: androidx.room.paging.LimitOffsetDataSource#loadRange(LoadRangeParams, LoadRangeCallback<T>):
+    Removed method androidx.room.paging.LimitOffsetDataSource.loadRange(LoadRangeParams,LoadRangeCallback<T>)
diff --git a/samples/Support4Demos/src/main/java/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java b/samples/Support4Demos/src/main/java/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java
index 0c30d5c..4e926c7 100644
--- a/samples/Support4Demos/src/main/java/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java
+++ b/samples/Support4Demos/src/main/java/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java
@@ -132,8 +132,7 @@
         setContentView(getLayoutId());
 
         // TODO use by viewModels() once this class switches to Kotlin
-        mViewModel = new ViewModelProvider(this,
-                new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);
+        mViewModel = new ViewModelProvider(this).get(MyViewModel.class);
         mViewModel.refreshDone.observe(this, event -> {
             if (event.getContentIfNotHandled() != null) {
                 mSwipeRefreshWidget.setRefreshing(false);
diff --git a/samples/Support7Demos/build.gradle b/samples/Support7Demos/build.gradle
index fdaed3e..eac5be8 100644
--- a/samples/Support7Demos/build.gradle
+++ b/samples/Support7Demos/build.gradle
@@ -10,6 +10,7 @@
     api 'com.google.android.material:material:1.0.0'
     implementation(project(":appcompat"))
     implementation(project(":cardview"))
+    implementation(project(":core:core"))
     implementation(project(":gridlayout"))
     implementation(project(":mediarouter"))
     implementation(project(":palette:palette"))
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/app/AlertDialogUsage.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/app/AlertDialogUsage.java
index 866c247..f32bfed 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/app/AlertDialogUsage.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/app/AlertDialogUsage.java
@@ -22,6 +22,7 @@
 
 import androidx.appcompat.app.AlertDialog;
 import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.content.res.AppCompatResources;
 
 import com.example.android.supportv7.Cheeses;
 import com.example.android.supportv7.R;
@@ -58,9 +59,12 @@
                 showSimpleButtonsDialog();
                 break;
             case 2:
-                showSingleChoiceDialog();
+                showSimpleWithMoreButtonsDialog();
                 break;
             case 3:
+                showSingleChoiceDialog();
+                break;
+            case 4:
                 showMultiChoiceDialog();
                 break;
         }
@@ -82,6 +86,19 @@
         b.show();
     }
 
+    private void showSimpleWithMoreButtonsDialog() {
+        AlertDialog.Builder b = new AlertDialog.Builder(this);
+        b.setTitle(R.string.dialog_title);
+        b.setMessage(R.string.dialog_content);
+        b.setNegativeButton("-ve", null);
+        b.setNegativeButtonIcon(AppCompatResources.getDrawable(this, R.drawable.ic_media_pause));
+        b.setNeutralButton("=ve", null);
+        b.setNeutralButtonIcon(AppCompatResources.getDrawable(this, R.drawable.ic_media_play));
+        b.setPositiveButton("+ve", null);
+        b.setPositiveButtonIcon(AppCompatResources.getDrawable(this, R.drawable.ic_media_stop));
+        b.show();
+    }
+
     private void showSingleChoiceDialog() {
         AlertDialog.Builder b = new AlertDialog.Builder(this);
         b.setTitle(R.string.dialog_title);
diff --git a/samples/Support7Demos/src/main/res/values/arrays.xml b/samples/Support7Demos/src/main/res/values/arrays.xml
index e610f1c..e020222 100644
--- a/samples/Support7Demos/src/main/res/values/arrays.xml
+++ b/samples/Support7Demos/src/main/res/values/arrays.xml
@@ -37,6 +37,7 @@
     <string-array name="alert_dialog_types">
         <item>Simple</item>
         <item>Simple with buttons</item>
+        <item>Simple with more buttons</item>
         <item>List (single choice)</item>
         <item>List (multi choice)</item>
     </string-array>
diff --git a/settings.gradle b/settings.gradle
index f94aa3f9..e953056 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -39,6 +39,10 @@
 includeProject(":activity:activity-ktx", "activity/activity-ktx")
 includeProject(":activity:integration-tests:testapp", "activity/integration-tests/testapp")
 includeProject(":ads-identifier", "ads/ads-identifier")
+includeProject(":ads-identifier:integration-tests:testapp", "ads/ads-identifier/integration-tests/testapp")
+includeProject(":ads-identifier-common", "ads/ads-identifier-common")
+includeProject(":ads-identifier-provider", "ads/ads-identifier-provider")
+includeProject(":ads-identifier-provider:integration-tests:testapp", "ads/ads-identifier-provider/integration-tests/testapp")
 includeProject(":annotation:annotation", "annotation/annotation")
 includeProject(":annotation:annotation-sampled", "annotation/annotation-sampled")
 includeProject(":annotation:annotation-experimental", "annotation/annotation-experimental")
@@ -55,7 +59,8 @@
 includeProject(":arch:core-runtime", "arch/core-runtime")
 includeProject(":asynclayoutinflater", "asynclayoutinflater")
 includeProject(":autofill", "autofill")
-includeProject(":benchmark", "benchmark")
+includeProject(":benchmark:benchmark-common", "benchmark/common")
+includeProject(":benchmark:benchmark-junit4", "benchmark/junit4")
 includeProject(":benchmark:benchmark-benchmark", "benchmark/benchmark")
 includeProject(":benchmark:benchmark-gradle-plugin", "benchmark/gradle-plugin")
 includeProject(":benchmark:integration-tests:startup-benchmark", "benchmark/integration-tests/startup-benchmark")
diff --git a/sharetarget/build.gradle b/sharetarget/build.gradle
index b750a70..cc27624 100644
--- a/sharetarget/build.gradle
+++ b/sharetarget/build.gradle
@@ -25,7 +25,7 @@
 }
 
 dependencies {
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     implementation("androidx.collection:collection:1.0.0")
     api(GUAVA_LISTENABLE_FUTURE)
     implementation("androidx.concurrent:concurrent-futures:1.0.0-alpha02")
diff --git a/slices/benchmark/build.gradle b/slices/benchmark/build.gradle
index b063a7b..f7cb0db 100644
--- a/slices/benchmark/build.gradle
+++ b/slices/benchmark/build.gradle
@@ -21,6 +21,7 @@
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
+    id("androidx.benchmark")
 }
 
 dependencies {
@@ -28,7 +29,7 @@
     androidTestImplementation(project(":slice-view"))
     androidTestImplementation(project(":slice-core"))
     androidTestImplementation(project(":slice-builders"))
-    androidTestImplementation(project(":benchmark"))
+    androidTestImplementation(project(":benchmark:benchmark-junit4"))
     androidTestImplementation(JUNIT)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/slices/benchmark/src/androidTest/java/androidx/slice/SliceSerializeMetrics.java b/slices/benchmark/src/androidTest/java/androidx/slice/SliceSerializeMetrics.java
index 0b26fbd..de9e87a 100644
--- a/slices/benchmark/src/androidTest/java/androidx/slice/SliceSerializeMetrics.java
+++ b/slices/benchmark/src/androidTest/java/androidx/slice/SliceSerializeMetrics.java
@@ -32,8 +32,8 @@
 import android.graphics.Canvas;
 import android.net.Uri;
 
-import androidx.benchmark.BenchmarkRule;
 import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.benchmark.test.R;
 import androidx.slice.core.SliceHints;
diff --git a/slices/benchmark/src/androidTest/java/androidx/slice/SliceViewMetrics.java b/slices/benchmark/src/androidTest/java/androidx/slice/SliceViewMetrics.java
index 43143e1..05482e8 100644
--- a/slices/benchmark/src/androidTest/java/androidx/slice/SliceViewMetrics.java
+++ b/slices/benchmark/src/androidTest/java/androidx/slice/SliceViewMetrics.java
@@ -20,8 +20,8 @@
 import android.content.Context;
 import android.net.Uri;
 
-import androidx.benchmark.BenchmarkRule;
 import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 import androidx.slice.widget.SliceView;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
diff --git a/slices/builders/api/restricted_1.1.0-alpha02.ignore b/slices/builders/api/restricted_1.1.0-alpha02.ignore
deleted file mode 100644
index 9235188..0000000
--- a/slices/builders/api/restricted_1.1.0-alpha02.ignore
+++ /dev/null
@@ -1,5 +0,0 @@
-// Baseline format: 1.0
-AddedAbstractMethod: androidx.slice.builders.impl.MessagingBuilder#add(androidx.slice.builders.impl.TemplateBuilderImpl):
-    Added method androidx.slice.builders.impl.MessagingBuilder.add(androidx.slice.builders.impl.TemplateBuilderImpl)
-AddedAbstractMethod: androidx.slice.builders.impl.MessagingBuilder#createMessageBuilder():
-    Added method androidx.slice.builders.impl.MessagingBuilder.createMessageBuilder()
diff --git a/slices/builders/build.gradle b/slices/builders/build.gradle
index 914633c..63bbe8d 100644
--- a/slices/builders/build.gradle
+++ b/slices/builders/build.gradle
@@ -28,7 +28,7 @@
     implementation(project(":slice-core"))
     api(project(":remotecallback"))
     implementation "androidx.annotation:annotation:1.1.0"
-    implementation "androidx.core:core:1.1.0-rc01"
+    implementation "androidx.core:core:1.1.0"
     implementation project(':collection:collection')
 }
 
diff --git a/slices/core/api/restricted_1.1.0-alpha02.ignore b/slices/core/api/restricted_1.1.0-alpha02.ignore
index 8a634d1..03bee74 100644
--- a/slices/core/api/restricted_1.1.0-alpha02.ignore
+++ b/slices/core/api/restricted_1.1.0-alpha02.ignore
@@ -1,12 +1,14 @@
 // Baseline format: 1.0
-RemovedClass: androidx.slice.compat.CompatPermissionManager.PermissionState:
-    Removed class androidx.slice.compat.CompatPermissionManager.PermissionState
-RemovedClass: androidx.slice.compat.SliceProviderWrapperContainer.SliceProviderWrapper:
-    Removed class androidx.slice.compat.SliceProviderWrapperContainer.SliceProviderWrapper
+AddedAbstractMethod: androidx.slice.SliceManager#getPinnedSpecs(android.net.Uri):
+    Added method androidx.slice.SliceManager.getPinnedSpecs(android.net.Uri)
 
 
+RemovedMethod: androidx.slice.SliceProvider#createPermissionIntent(android.content.Context, android.net.Uri, String):
+    Removed method androidx.slice.SliceProvider.createPermissionIntent(android.content.Context,android.net.Uri,String)
 RemovedMethod: androidx.slice.SliceProvider#createPermissionSlice(android.content.Context, android.net.Uri, String):
     Removed method androidx.slice.SliceProvider.createPermissionSlice(android.content.Context,android.net.Uri,String)
+RemovedMethod: androidx.slice.SliceProvider#getPermissionString(android.content.Context, String):
+    Removed method androidx.slice.SliceProvider.getPermissionString(android.content.Context,String)
 RemovedMethod: androidx.slice.core.SliceQuery#stream(androidx.slice.Slice):
     Removed method androidx.slice.core.SliceQuery.stream(androidx.slice.Slice)
 RemovedMethod: androidx.slice.core.SliceQuery#stream(androidx.slice.SliceItem):
diff --git a/slices/core/src/androidTest/java/androidx/slice/compat/CompatPermissionManagerTest.java b/slices/core/src/androidTest/java/androidx/slice/compat/CompatPermissionManagerTest.java
index 97f2bea..583cb44 100644
--- a/slices/core/src/androidTest/java/androidx/slice/compat/CompatPermissionManagerTest.java
+++ b/slices/core/src/androidTest/java/androidx/slice/compat/CompatPermissionManagerTest.java
@@ -36,6 +36,7 @@
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SdkSuppress;
 
@@ -51,6 +52,7 @@
 
     private final Context mContext = ApplicationProvider.getApplicationContext();
 
+    @FlakyTest
     @Test
     public void testAutoGrant() {
         final Uri uri = new Uri.Builder()
diff --git a/slidingpanelayout/build.gradle b/slidingpanelayout/build.gradle
index b9732d5..7738665 100644
--- a/slidingpanelayout/build.gradle
+++ b/slidingpanelayout/build.gradle
@@ -9,7 +9,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    implementation("androidx.core:core:1.1.0-rc01")
+    implementation("androidx.core:core:1.1.0")
     api(project(":customview"))
 }
 
diff --git a/swiperefreshlayout/build.gradle b/swiperefreshlayout/build.gradle
index 1309851..46c3da4 100644
--- a/swiperefreshlayout/build.gradle
+++ b/swiperefreshlayout/build.gradle
@@ -10,7 +10,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
     api("androidx.interpolator:interpolator:1.0.0")
 
     androidTestImplementation(JUNIT)
diff --git a/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeInjector.java b/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeInjector.java
deleted file mode 100644
index 20ef90b..0000000
--- a/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeInjector.java
+++ /dev/null
@@ -1,125 +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.swiperefreshlayout.widget;
-
-import static android.os.SystemClock.uptimeMillis;
-
-import android.app.Instrumentation;
-import android.os.SystemClock;
-import android.view.MotionEvent;
-import android.view.View;
-
-import androidx.test.espresso.action.CoordinatesProvider;
-
-public class SwipeInjector {
-
-    private static final int X = 0;
-    private static final int Y = 1;
-
-    private Instrumentation mInstrumentation;
-
-    private long mStartTime = 0;
-    private long mCurrentTime = 0;
-    private float[] mFloats = new float[2];
-
-    public SwipeInjector(Instrumentation instrumentation) {
-        mInstrumentation = instrumentation;
-    }
-
-    public void startDrag(CoordinatesProvider provider, View view) {
-        float[] floats = provider.calculateCoordinates(view);
-        startDrag(floats[X], floats[Y]);
-    }
-
-    public void startDrag(float x, float y) {
-        mStartTime = uptimeMillis();
-        mFloats[X] = x;
-        mFloats[Y] = y;
-        injectMotionEvent(obtainDownEvent(mStartTime, mFloats));
-    }
-
-    public void dragTo(CoordinatesProvider provider, View view) {
-        float[] floats = provider.calculateCoordinates(view);
-        dragTo(floats[X], floats[Y]);
-    }
-
-    public void dragTo(float x, float y) {
-        mFloats[X] = x;
-        mFloats[Y] = y;
-        injectMotionEvent(obtainMoveEvent(mStartTime, uptimeMillis(), mFloats));
-    }
-
-    public void dragTo(CoordinatesProvider provider, View view, long duration) {
-        float[] floats = provider.calculateCoordinates(view);
-        dragTo(floats[X], floats[Y], duration);
-    }
-
-    public void dragTo(float x, float y, long duration) {
-        float x0 = mFloats[X];
-        float y0 = mFloats[Y];
-        float dx = x - x0;
-        float dy = y - y0;
-        int steps = Math.max(1, Math.round(duration / 10f));
-        for (int i = 1; i <= steps; i++) {
-            float progress = (float) i / steps;
-            mFloats[X] = x0 + dx * progress;
-            mFloats[Y] = y0 + dy * progress;
-            injectMotionEvent(obtainMoveEvent(mStartTime, mCurrentTime + 10L, mFloats));
-        }
-    }
-
-    public void dragBy(float dx, float dy) {
-        dragTo(mFloats[X] + dx, mFloats[Y] + dy);
-    }
-
-    public void dragBy(float dx, float dy, long duration) {
-        dragTo(mFloats[X] + dx, mFloats[Y] + dy, duration);
-    }
-
-    public void finishDrag() {
-        injectMotionEvent(obtainUpEvent(mStartTime, uptimeMillis(), mFloats));
-    }
-
-    private static MotionEvent obtainDownEvent(long time, float[] coord) {
-        return MotionEvent.obtain(time, time,
-                MotionEvent.ACTION_DOWN, coord[X], coord[Y], 0);
-    }
-
-    private static MotionEvent obtainMoveEvent(long startTime, long time, float[] coord) {
-        return MotionEvent.obtain(startTime, time,
-                MotionEvent.ACTION_MOVE, coord[X], coord[Y], 0);
-    }
-
-    private static MotionEvent obtainUpEvent(long startTime, long time, float[] coord) {
-        return MotionEvent.obtain(startTime, time,
-                MotionEvent.ACTION_UP, coord[X], coord[Y], 0);
-    }
-
-    private void injectMotionEvent(MotionEvent event) {
-        try {
-            long eventTime = event.getEventTime();
-            long now = uptimeMillis();
-            if (eventTime - now > 0) {
-                SystemClock.sleep(eventTime - now);
-            }
-            mInstrumentation.sendPointerSync(event);
-            mCurrentTime = eventTime;
-        } finally {
-            event.recycle();
-        }
-    }
-}
diff --git a/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutInHorizontallyScrollingParentTest.java b/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutInHorizontallyScrollingParentTest.java
index f9c514b..665fbb4 100644
--- a/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutInHorizontallyScrollingParentTest.java
+++ b/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/SwipeRefreshLayoutInHorizontallyScrollingParentTest.java
@@ -33,6 +33,7 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
 import androidx.testutils.PollingCheck;
+import androidx.testutils.SwipeInjector;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/testutils/src/main/java/androidx/testutils/SimpleGestureGenerator.kt b/testutils/src/main/java/androidx/testutils/SimpleGestureGenerator.kt
index 72e7cf3..da76ecc 100644
--- a/testutils/src/main/java/androidx/testutils/SimpleGestureGenerator.kt
+++ b/testutils/src/main/java/androidx/testutils/SimpleGestureGenerator.kt
@@ -58,23 +58,41 @@
 }
 
 fun MotionEventData.toMotionEvent(downTime: Long): MotionEvent = MotionEvent.obtain(
-        downTime,
-        this.eventTimeDelta + downTime,
-        this.action,
-        this.x,
-        this.y,
-        this.metaState)
+    downTime,
+    this.eventTimeDelta + downTime,
+    this.action,
+    this.x,
+    this.y,
+    this.metaState
+)
 
 /**
- * Constructs a [FlingData] from a [Context].
+ * Constructs a [FlingData] from a [Context] and [velocityPixelsPerSecond].
+ *
+ * [velocityPixelsPerSecond] must between [ViewConfiguration.getScaledMinimumFlingVelocity] * 1.1
+ * and [ViewConfiguration.getScaledMaximumFlingVelocity] * .9, inclusive.  Losses of precision do
+ * not allow the simulated fling to be super precise.
  */
-fun generateFlingData(context: Context): FlingData {
+@JvmOverloads
+fun generateFlingData(context: Context, velocityPixelsPerSecond: Float? = null): FlingData {
     val configuration = ViewConfiguration.get(context)
     val touchSlop = configuration.scaledTouchSlop
     val minimumVelocity = configuration.scaledMinimumFlingVelocity
     val maximumVelocity = configuration.scaledMaximumFlingVelocity
 
-    val targetPixelsPerMilli = ((maximumVelocity + minimumVelocity) / 2) / 1000f
+    val targetPixelsPerMilli =
+        if (velocityPixelsPerSecond != null) {
+            if (velocityPixelsPerSecond < minimumVelocity * 1.1 - .001f ||
+                velocityPixelsPerSecond > maximumVelocity * .9 + .001f) {
+                throw IllegalArgumentException("velocityPixelsPerSecond must be between " +
+                        "ViewConfiguration.scaledMinimumFlingVelocity * 1.1 and " +
+                        "ViewConfiguration.scaledMinimumFlingVelocity * .9, inclusive")
+            }
+            velocityPixelsPerSecond / 1000f
+        } else {
+            ((maximumVelocity + minimumVelocity) / 2) / 1000f
+        }
+
     val targetDistancePixels = touchSlop * 2
     val targetMillisPassed = floor(targetDistancePixels / targetPixelsPerMilli).toInt()
 
@@ -89,7 +107,7 @@
  *  Returns [value] rounded up to the closest [interval] * N, where N is a Integer.
  */
 private fun ceilToInterval(value: Int, interval: Int): Int =
-        ceil(value.toFloat() / interval).toInt() * interval
+    ceil(value.toFloat() / interval).toInt() * interval
 
 /**
  * Generates a [List] of [MotionEventData] starting from ([originX], [originY]) that will cause a
@@ -100,7 +118,7 @@
     originY: Float,
     fingerDirection: Direction
 ):
-    List<MotionEventData> {
+        List<MotionEventData> {
 
     // Ceiling the time and distance to match up with motion event intervals.
     val time: Int = ceilToInterval(this.time, MOTION_EVENT_INTERVAL_MILLIS)
@@ -127,8 +145,8 @@
     motionEventData.add(MotionEventData(0, MotionEvent.ACTION_DOWN, originX, originY, 0))
     for (i in 1..(numberOfInnerEvents)) {
         val timeDelta = i * MOTION_EVENT_INTERVAL_MILLIS
-        val x = i * dxIncrement
-        val y = i * dyIncrement
+        val x = originX + (i * dxIncrement)
+        val y = originY + (i * dyIncrement)
         motionEventData.add(MotionEventData(timeDelta, MotionEvent.ACTION_MOVE, x, y, 0))
     }
     motionEventData.add(MotionEventData(time, MotionEvent.ACTION_MOVE, toX, toY, 0))
@@ -161,12 +179,17 @@
  * @see [generateFlingMotionEventData]
  * @see [dispatchTouchEvents]
  */
+@JvmOverloads
 fun View.simulateFling(
     downTime: Long,
     originX: Float,
     originY: Float,
-    direction: Direction
+    direction: Direction,
+    velocityPixelsPerSecond: Float? = null
 ) {
-    dispatchTouchEvents(downTime,
-            generateFlingData(context).generateFlingMotionEventData(originX, originY, direction))
+    dispatchTouchEvents(
+        downTime,
+        generateFlingData(context, velocityPixelsPerSecond)
+            .generateFlingMotionEventData(originX, originY, direction)
+    )
 }
diff --git a/testutils/src/main/java/androidx/testutils/SwipeInjector.java b/testutils/src/main/java/androidx/testutils/SwipeInjector.java
new file mode 100644
index 0000000..9c63a7d
--- /dev/null
+++ b/testutils/src/main/java/androidx/testutils/SwipeInjector.java
@@ -0,0 +1,234 @@
+/*
+ * 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.testutils;
+
+import static android.os.SystemClock.uptimeMillis;
+
+import android.annotation.SuppressLint;
+import android.app.Instrumentation;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.test.espresso.action.CoordinatesProvider;
+
+/**
+ * Injects motion events for custom gestures using Instrumentation. Use this to "draw" the gestures,
+ * in a similar way as a Path is defined. Create an instance of this class, start a drag, move it
+ * around and then finish it. For example, this injects a "Swipe right then left" gesture from
+ * TalkBack:
+ *
+ * <pre>
+ *     View view = findViewById(R.id.view_to_swipe_on);
+ *
+ *     SwipeInjector swiper = new SwipeInjector(InstrumentationRegistry.getInstrumentation());
+ *     swiper.startDrag(GeneralLocation.LEFT, view);      // Start at the left side of the view
+ *     swiper.dragTo(GeneralLocation.RIGHT, view, 200);   // Swipe to the right in 200 ms
+ *     swiper.dragTo(GeneralLocation.LEFT, view, 200);    // Swipe to the left in 200 ms
+ *     swiper.finishDrag();                               // Finish the gesture
+ * </pre>
+ *
+ * @see androidx.test.espresso.action.GeneralLocation GeneralLocation
+ * @see TranslatedCoordinatesProvider
+ */
+public class SwipeInjector {
+
+    private static final int X = 0;
+    private static final int Y = 1;
+
+    private Instrumentation mInstrumentation;
+
+    private long mStartTime = 0;
+    private long mCurrentTime = 0;
+    private float[] mFloats = new float[2];
+
+    /**
+     * Creates an injector.
+     */
+    public SwipeInjector(@NonNull Instrumentation instrumentation) {
+        mInstrumentation = instrumentation;
+    }
+
+    /**
+     * Start a drag on the coordinate provided for the given view. For example,
+     * {@code startDrag(GeneralLocation.CENTER, mView)}.
+     *
+     * @param provider provider of the coordinate of the pointer down event, like
+     *        {@link androidx.test.espresso.action.GeneralLocation#CENTER GeneralLocation.CENTER}
+     * @param view the view passed to the
+     *        {@link CoordinatesProvider#calculateCoordinates(View) coordinates provider}
+     */
+    @SuppressLint("LambdaLast")
+    public void startDrag(@NonNull CoordinatesProvider provider, @NonNull View view) {
+        float[] floats = provider.calculateCoordinates(view);
+        startDrag(floats[X], floats[Y]);
+    }
+
+    /**
+     * Start a drag on the given coordinate. For example, {@code startDrag(100, 200)}. Note that the
+     * coordinates are absolute coordinates for the screen.
+     *
+     * @param x the x coordinate of the pointer down event
+     * @param y the y coordinate of the pointer down event
+     */
+    public void startDrag(float x, float y) {
+        mStartTime = uptimeMillis();
+        mFloats[X] = x;
+        mFloats[Y] = y;
+        injectMotionEvent(obtainDownEvent(mStartTime, mFloats));
+    }
+
+    /**
+     * Extend the drag with a single motion event to the coordinates provided for the given view.
+     * For example, {@code dragTo(GeneralLocation.LEFT, mView)}. Call one of the {@code startDrag}
+     * methods before calling any of the {@code dragTo} methods.
+     *
+     * @param provider provider of the coordinate of the pointer move event, like
+     *        {@link androidx.test.espresso.action.GeneralLocation#CENTER GeneralLocation.CENTER}
+     * @param view the view passed to the
+     *        {@link CoordinatesProvider#calculateCoordinates(View) coordinates provider}
+     */
+    @SuppressLint("LambdaLast")
+    public void dragTo(@NonNull CoordinatesProvider provider, @NonNull View view) {
+        float[] floats = provider.calculateCoordinates(view);
+        dragTo(floats[X], floats[Y]);
+    }
+
+    /**
+     * Extend the drag with a single motion event to the given coordinate. For example,
+     * {@code dragTo(0, 10)}. Call one of the {@code startDrag} methods before calling any of the
+     * {@code dragTo} methods. Note that the coordinates are absolute coordinates for the screen.
+     *
+     * @param x the x coordinate of the pointer move event
+     * @param y the y coordinate of the pointer move event
+     */
+    public void dragTo(float x, float y) {
+        mFloats[X] = x;
+        mFloats[Y] = y;
+        injectMotionEvent(obtainMoveEvent(mStartTime, uptimeMillis(), mFloats));
+    }
+
+    /**
+     * Extend the drag with a range of motion events to the coordinate provided for the given view.
+     * An event will be injected every 10ms, interpolating linearly to the destination. For example,
+     * {@code dragTo(GeneralLocation.LEFT, mView, 300)}. Call one of the {@code startDrag} methods
+     * before calling any of the {@code dragTo} methods.
+     *
+     * @param provider provider of the final coordinate of this part of the drag, like
+     *        {@link androidx.test.espresso.action.GeneralLocation#CENTER GeneralLocation.CENTER}
+     * @param view the view passed to the
+     *        {@link CoordinatesProvider#calculateCoordinates(View) coordinates provider}
+     * @param duration the time in milliseconds this part of the drag should take. Actual time will
+     *        be different if not a multiple of 10.
+     */
+    @SuppressLint("LambdaLast")
+    public void dragTo(@NonNull CoordinatesProvider provider, @NonNull View view, long duration) {
+        float[] floats = provider.calculateCoordinates(view);
+        dragTo(floats[X], floats[Y], duration);
+    }
+
+    /**
+     * Extend the drag with a range of motion events to the given coordinate. An event will be
+     * injected every 10ms, interpolating linearly to the destination. For example,
+     * {@code dragTo(10, 0, 350)}. Call one of the {@code startDrag} methods before calling any of
+     * the {@code dragTo} methods. Note that the coordinates are absolute coordinates for the
+     * screen.
+     *
+     * @param x the final x coordinate of this part of the drag
+     * @param y the final y coordinate of this part of the drag
+     * @param duration the time in milliseconds this part of the drag should take. Actual time will
+     *        be different if not a multiple of 10.
+     */
+    public void dragTo(float x, float y, long duration) {
+        float x0 = mFloats[X];
+        float y0 = mFloats[Y];
+        float dx = x - x0;
+        float dy = y - y0;
+        int steps = Math.max(1, Math.round(duration / 10f));
+        for (int i = 1; i <= steps; i++) {
+            float progress = (float) i / steps;
+            mFloats[X] = x0 + dx * progress;
+            mFloats[Y] = y0 + dy * progress;
+            injectMotionEvent(obtainMoveEvent(mStartTime, mCurrentTime + 10L, mFloats));
+        }
+    }
+
+    /**
+     * Extend the drag with a single motion event covering the given delta. For example,
+     * {@code dragBy(10, -20)}. Call one of the {@code startDrag} methods before calling any of
+     * the {@code dragBy} methods.
+     *
+     * @param dx the distance in x direction
+     * @param dy the distance in y direction
+     */
+    public void dragBy(float dx, float dy) {
+        dragTo(mFloats[X] + dx, mFloats[Y] + dy);
+    }
+
+    /**
+     * Extend the drag with a range of motion events covering the given delta. An event will be
+     * injected every 10ms, interpolating linearly to the destination. For example,
+     * {@code dragBy(-50, 0, 100)}. Call one of the {@code startDrag} methods before calling any of
+     * the {@code dragBy} methods.
+     *
+     * @param dx the distance in x direction
+     * @param dy the distance in y direction
+     * @param duration the time in milliseconds this part of the drag should take. Actual time will
+     *        be different if not a multiple of 10.
+     */
+    public void dragBy(float dx, float dy, long duration) {
+        dragTo(mFloats[X] + dx, mFloats[Y] + dy, duration);
+    }
+
+    /**
+     * Finish the drag with a pointer up event. A new drag can be started after this with one of the
+     * {@code startDrag} methods.
+     */
+    public void finishDrag() {
+        injectMotionEvent(obtainUpEvent(mStartTime, uptimeMillis(), mFloats));
+    }
+
+    private static MotionEvent obtainDownEvent(long time, float[] coord) {
+        return MotionEvent.obtain(time, time,
+                MotionEvent.ACTION_DOWN, coord[X], coord[Y], 0);
+    }
+
+    private static MotionEvent obtainMoveEvent(long startTime, long time, float[] coord) {
+        return MotionEvent.obtain(startTime, time,
+                MotionEvent.ACTION_MOVE, coord[X], coord[Y], 0);
+    }
+
+    private static MotionEvent obtainUpEvent(long startTime, long time, float[] coord) {
+        return MotionEvent.obtain(startTime, time,
+                MotionEvent.ACTION_UP, coord[X], coord[Y], 0);
+    }
+
+    private void injectMotionEvent(MotionEvent event) {
+        try {
+            long eventTime = event.getEventTime();
+            long now = uptimeMillis();
+            if (eventTime - now > 0) {
+                SystemClock.sleep(eventTime - now);
+            }
+            mInstrumentation.sendPointerSync(event);
+            mCurrentTime = eventTime;
+        } finally {
+            event.recycle();
+        }
+    }
+}
diff --git a/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/TranslatedCoordinatesProvider.java b/testutils/src/main/java/androidx/testutils/TranslatedCoordinatesProvider.java
similarity index 65%
rename from swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/TranslatedCoordinatesProvider.java
rename to testutils/src/main/java/androidx/testutils/TranslatedCoordinatesProvider.java
index 4cdd93b..a0eaebd 100644
--- a/swiperefreshlayout/src/androidTest/java/androidx/swiperefreshlayout/widget/TranslatedCoordinatesProvider.java
+++ b/testutils/src/main/java/androidx/testutils/TranslatedCoordinatesProvider.java
@@ -14,18 +14,32 @@
  * limitations under the License.
  */
 
-package androidx.swiperefreshlayout.widget;
+package androidx.testutils;
 
+import android.annotation.SuppressLint;
 import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.test.espresso.action.CoordinatesProvider;
 
+/**
+ * Translates a {@link CoordinatesProvider} by the given x and y distances. The distances are given
+ * in pixels. Common providers to start with can be found in
+ * {@link androidx.test.espresso.action.GeneralLocation GeneralLocation}.
+ */
 public class TranslatedCoordinatesProvider implements CoordinatesProvider {
     private CoordinatesProvider mProvider;
     private float mDx;
     private float mDy;
 
+    /**
+     * Creates an instance of {@link TranslatedCoordinatesProvider}
+     *
+     * @param coordinatesProvider the {@link CoordinatesProvider} to translate
+     * @param dx the distance in x direction
+     * @param dy the distance in y direction
+     */
+    @SuppressLint("LambdaLast")
     public TranslatedCoordinatesProvider(@NonNull CoordinatesProvider coordinatesProvider, float dx,
             float dy) {
         mProvider = coordinatesProvider;
@@ -33,8 +47,9 @@
         mDy = dy;
     }
 
+    @NonNull
     @Override
-    public float[] calculateCoordinates(View view) {
+    public float[] calculateCoordinates(@NonNull View view) {
         float[] coords = mProvider.calculateCoordinates(view);
         coords[0] += mDx;
         coords[1] += mDy;
diff --git a/textclassifier/api/1.0.0-alpha03.txt b/textclassifier/api/1.0.0-alpha03.txt
index 974299f..21567f3 100644
--- a/textclassifier/api/1.0.0-alpha03.txt
+++ b/textclassifier/api/1.0.0-alpha03.txt
@@ -2,7 +2,7 @@
 package androidx.textclassifier {
 
   public final class ConversationAction {
-    method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle!);
+    method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle);
     method public androidx.core.app.RemoteActionCompat? getAction();
     method @FloatRange(from=0, to=1) public float getConfidenceScore();
     method public android.os.Bundle getExtras();
diff --git a/textclassifier/api/current.txt b/textclassifier/api/current.txt
index 974299f..21567f3 100644
--- a/textclassifier/api/current.txt
+++ b/textclassifier/api/current.txt
@@ -2,7 +2,7 @@
 package androidx.textclassifier {
 
   public final class ConversationAction {
-    method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle!);
+    method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle);
     method public androidx.core.app.RemoteActionCompat? getAction();
     method @FloatRange(from=0, to=1) public float getConfidenceScore();
     method public android.os.Bundle getExtras();
diff --git a/textclassifier/api/restricted_1.0.0-alpha03.txt b/textclassifier/api/restricted_1.0.0-alpha03.txt
index e335921..2bfef3d 100644
--- a/textclassifier/api/restricted_1.0.0-alpha03.txt
+++ b/textclassifier/api/restricted_1.0.0-alpha03.txt
@@ -2,7 +2,7 @@
 package androidx.textclassifier {
 
   public final class ConversationAction {
-    method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle!);
+    method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle);
     method public androidx.core.app.RemoteActionCompat? getAction();
     method @FloatRange(from=0, to=1) public float getConfidenceScore();
     method public android.os.Bundle getExtras();
diff --git a/textclassifier/api/restricted_current.txt b/textclassifier/api/restricted_current.txt
index e335921..2bfef3d 100644
--- a/textclassifier/api/restricted_current.txt
+++ b/textclassifier/api/restricted_current.txt
@@ -2,7 +2,7 @@
 package androidx.textclassifier {
 
   public final class ConversationAction {
-    method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle!);
+    method public static androidx.textclassifier.ConversationAction createFromBundle(android.os.Bundle);
     method public androidx.core.app.RemoteActionCompat? getAction();
     method @FloatRange(from=0, to=1) public float getConfidenceScore();
     method public android.os.Bundle getExtras();
diff --git a/textclassifier/build.gradle b/textclassifier/build.gradle
index d2cd121..92508eb 100644
--- a/textclassifier/build.gradle
+++ b/textclassifier/build.gradle
@@ -13,7 +13,7 @@
     api("androidx.annotation:annotation:1.1.0")
     implementation("androidx.collection:collection:1.0.0")
     // TODO: change to 1.1.0-alpha04 after release
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/textclassifier/src/main/java/androidx/textclassifier/ConversationAction.java b/textclassifier/src/main/java/androidx/textclassifier/ConversationAction.java
index 9aa9288..866e49c 100644
--- a/textclassifier/src/main/java/androidx/textclassifier/ConversationAction.java
+++ b/textclassifier/src/main/java/androidx/textclassifier/ConversationAction.java
@@ -32,7 +32,12 @@
 
 import java.lang.annotation.Retention;
 
-/** Represents the action suggested by a {@link TextClassifier} on a given conversation. */
+/**
+ * Represents an action suggested by a {@link TextClassifier} on a given conversation.
+ *
+ * @see TextClassifier#suggestConversationActions(ConversationActions.Request)
+ * @see ConversationActions
+ */
 public final class ConversationAction {
 
     private static final String EXTRA_TYPE = "type";
@@ -214,7 +219,7 @@
      * Converts a bundle that was created using {@link #toBundle()} to a {@link ConversationAction}.
      */
     @NonNull
-    public static ConversationAction createFromBundle(Bundle bundle) {
+    public static ConversationAction createFromBundle(@NonNull Bundle bundle) {
         return new ConversationAction(
                 bundle.getString(EXTRA_TYPE),
                 (RemoteActionCompat) ParcelUtils.getVersionedParcelable(bundle, EXTRA_ACTION),
diff --git a/textclassifier/src/main/java/androidx/textclassifier/ConversationActions.java b/textclassifier/src/main/java/androidx/textclassifier/ConversationActions.java
index d7129dc..3ba6f20 100644
--- a/textclassifier/src/main/java/androidx/textclassifier/ConversationActions.java
+++ b/textclassifier/src/main/java/androidx/textclassifier/ConversationActions.java
@@ -36,6 +36,8 @@
 
 /**
  * Represents a list of actions suggested by a {@link TextClassifier} on a given conversation.
+ * <p>
+ * This is an object to store the result of {@link TextClassifier#suggestConversationActions(Request)}.
  *
  * @see TextClassifier#suggestConversationActions(Request)
  */
diff --git a/textclassifier/src/main/java/androidx/textclassifier/widget/ToolbarController.java b/textclassifier/src/main/java/androidx/textclassifier/widget/ToolbarController.java
index 26f0c7f..45c481f 100644
--- a/textclassifier/src/main/java/androidx/textclassifier/widget/ToolbarController.java
+++ b/textclassifier/src/main/java/androidx/textclassifier/widget/ToolbarController.java
@@ -226,18 +226,18 @@
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic access */
-    static void updateRectCoordinates(Rect rect, TextView textView, int start, int end) {
-        final int[] startXY = getCoordinates(textView, start);
-        final int[] endXY = getCoordinates(textView, end);
+    static void updateRectCoordinates(Rect rect, TextView textView, int startIndex, int endIndex) {
+        final int[] startXY = getCoordinates(textView, startIndex, /* startCoordinate= */ true);
+        final int[] endXY = getCoordinates(textView, endIndex, /* startCoordinate= */false);
         rect.set(startXY[0], startXY[1], endXY[0], endXY[1]);
         rect.sort();
     }
 
-    private static int[] getCoordinates(TextView textView, int index) {
+    private static int[] getCoordinates(TextView textView, int index, boolean startCoordinate) {
         final Layout layout = textView.getLayout();
         final int line = layout.getLineForOffset(index);
         final int x = (int) layout.getPrimaryHorizontal(index);
-        final int y = layout.getLineTop(line);
+        final int y = (startCoordinate) ? layout.getLineTop(line) : layout.getLineBottom(line);
         final int[] xy = new int[2];
         textView.getLocationOnScreen(xy);
         return new int[]{
diff --git a/tv-provider/api/1.1.0-alpha01.ignore b/tv-provider/api/1.1.0-alpha01.ignore
deleted file mode 100644
index 7690c60..0000000
--- a/tv-provider/api/1.1.0-alpha01.ignore
+++ /dev/null
@@ -1,7 +0,0 @@
-// Baseline format: 1.0
-HiddenSuperclass: androidx.tvprovider.media.tv.PreviewProgram:
-    Public class androidx.tvprovider.media.tv.PreviewProgram stripped of unavailable superclass androidx.tvprovider.media.tv.BasePreviewProgram
-HiddenSuperclass: androidx.tvprovider.media.tv.Program:
-    Public class androidx.tvprovider.media.tv.Program stripped of unavailable superclass androidx.tvprovider.media.tv.BaseProgram
-HiddenSuperclass: androidx.tvprovider.media.tv.WatchNextProgram:
-    Public class androidx.tvprovider.media.tv.WatchNextProgram stripped of unavailable superclass androidx.tvprovider.media.tv.BasePreviewProgram
diff --git a/tv-provider/api/restricted_1.1.0-alpha01.ignore b/tv-provider/api/restricted_1.1.0-alpha01.ignore
deleted file mode 100644
index 10c7976..0000000
--- a/tv-provider/api/restricted_1.1.0-alpha01.ignore
+++ /dev/null
@@ -1,15 +0,0 @@
-// Baseline format: 1.0
-RemovedClass: androidx.tvprovider.media.tv.BasePreviewProgram.AspectRatio:
-    Removed class androidx.tvprovider.media.tv.BasePreviewProgram.AspectRatio
-RemovedClass: androidx.tvprovider.media.tv.BasePreviewProgram.Availability:
-    Removed class androidx.tvprovider.media.tv.BasePreviewProgram.Availability
-RemovedClass: androidx.tvprovider.media.tv.BasePreviewProgram.Builder:
-    Removed class androidx.tvprovider.media.tv.BasePreviewProgram.Builder
-RemovedClass: androidx.tvprovider.media.tv.BasePreviewProgram.InteractionType:
-    Removed class androidx.tvprovider.media.tv.BasePreviewProgram.InteractionType
-RemovedClass: androidx.tvprovider.media.tv.BasePreviewProgram.TvSeriesItemType:
-    Removed class androidx.tvprovider.media.tv.BasePreviewProgram.TvSeriesItemType
-RemovedClass: androidx.tvprovider.media.tv.BasePreviewProgram.Type:
-    Removed class androidx.tvprovider.media.tv.BasePreviewProgram.Type
-RemovedClass: androidx.tvprovider.media.tv.BaseProgram.Builder:
-    Removed class androidx.tvprovider.media.tv.BaseProgram.Builder
diff --git a/tv-provider/build.gradle b/tv-provider/build.gradle
index b4cbc11..902b0d5 100644
--- a/tv-provider/build.gradle
+++ b/tv-provider/build.gradle
@@ -10,7 +10,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
     androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/ui/integration-tests/benchmark/build.gradle b/ui/integration-tests/benchmark/build.gradle
index 4d8845b..ab94dc5 100644
--- a/ui/integration-tests/benchmark/build.gradle
+++ b/ui/integration-tests/benchmark/build.gradle
@@ -24,12 +24,14 @@
     id("com.android.library")
     id("AndroidXUiPlugin")
     id("org.jetbrains.kotlin.android")
+    id("androidx.benchmark")
 }
 
 dependencies {
     kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
     implementation(project(":ui:integration-tests:test"))
-    implementation(project(":benchmark"))
+    implementation(project(":benchmark:benchmark-junit4"))
+    implementation(project(":compose:compose-runtime"))
     implementation(KOTLIN_COMPOSE_STDLIB)
     implementation(JUNIT)
     androidTestImplementation(project(":ui:ui-core"))
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/CheckboxesInRowsBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/CheckboxesInRowsBenchmark.kt
index 994e303..58e7938 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/CheckboxesInRowsBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/CheckboxesInRowsBenchmark.kt
@@ -17,10 +17,14 @@
 package androidx.ui.benchmark.test
 
 import android.app.Activity
-import androidx.benchmark.BenchmarkRule
+import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
 import androidx.test.rule.ActivityTestRule
 import androidx.ui.benchmark.measureDrawPerf
+import androidx.ui.benchmark.measureFirstCompose
+import androidx.ui.benchmark.measureFirstDraw
+import androidx.ui.benchmark.measureFirstLayout
+import androidx.ui.benchmark.measureFirstMeasure
 import androidx.ui.benchmark.measureLayoutPerf
 import androidx.ui.benchmark.toggleStateMeasureDraw
 import androidx.ui.benchmark.toggleStateMeasureLayout
@@ -58,6 +62,30 @@
     private val activity: Activity get() = activityRule.activity
 
     @Test
+    fun first_compose() {
+        benchmarkRule.measureFirstCompose(activity,
+            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+    }
+
+    @Test
+    fun first_measure() {
+        benchmarkRule.measureFirstMeasure(activity,
+            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+    }
+
+    @Test
+    fun first_layout() {
+        benchmarkRule.measureFirstLayout(activity,
+            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+    }
+
+    @Test
+    fun first_draw() {
+        benchmarkRule.measureFirstDraw(activity,
+            CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
+    }
+
+    @Test
     fun toggleCheckbox_recompose() {
         benchmarkRule.toggleStateMeasureRecompose(activity,
             CheckboxesInRowsTestCase(activity, numberOfCheckboxes))
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnBenchmark.kt
index 1383456..85a5559 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnBenchmark.kt
@@ -17,10 +17,14 @@
 package androidx.ui.benchmark.test
 
 import android.app.Activity
-import androidx.benchmark.BenchmarkRule
+import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
 import androidx.test.rule.ActivityTestRule
 import androidx.ui.benchmark.measureDrawPerf
+import androidx.ui.benchmark.measureFirstCompose
+import androidx.ui.benchmark.measureFirstDraw
+import androidx.ui.benchmark.measureFirstLayout
+import androidx.ui.benchmark.measureFirstMeasure
 import androidx.ui.benchmark.measureLayoutPerf
 import androidx.ui.benchmark.toggleStateMeasureDraw
 import androidx.ui.benchmark.toggleStateMeasureLayout
@@ -58,6 +62,30 @@
     private val activity: Activity get() = activityRule.activity
 
     @Test
+    fun first_compose() {
+        benchmarkRule.measureFirstCompose(activity,
+            RectsInColumnTestCase(activity, numberOfRectangles))
+    }
+
+    @Test
+    fun first_measure() {
+        benchmarkRule.measureFirstMeasure(activity,
+            RectsInColumnTestCase(activity, numberOfRectangles))
+    }
+
+    @Test
+    fun first_layout() {
+        benchmarkRule.measureFirstLayout(activity,
+            RectsInColumnTestCase(activity, numberOfRectangles))
+    }
+
+    @Test
+    fun first_draw() {
+        benchmarkRule.measureFirstDraw(activity,
+            RectsInColumnTestCase(activity, numberOfRectangles))
+    }
+
+    @Test
     fun toggleRectangleColor_recompose() {
         benchmarkRule.toggleStateMeasureRecompose(activity,
             RectsInColumnTestCase(activity, numberOfRectangles))
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnSharedModelBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnSharedModelBenchmark.kt
index ba0502e..17a739a 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnSharedModelBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RectsInColumnSharedModelBenchmark.kt
@@ -17,10 +17,14 @@
 package androidx.ui.benchmark.test
 
 import android.app.Activity
-import androidx.benchmark.BenchmarkRule
+import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
 import androidx.test.rule.ActivityTestRule
 import androidx.ui.benchmark.measureDrawPerf
+import androidx.ui.benchmark.measureFirstCompose
+import androidx.ui.benchmark.measureFirstDraw
+import androidx.ui.benchmark.measureFirstLayout
+import androidx.ui.benchmark.measureFirstMeasure
 import androidx.ui.benchmark.measureLayoutPerf
 import androidx.ui.benchmark.toggleStateMeasureDraw
 import androidx.ui.benchmark.toggleStateMeasureLayout
@@ -58,6 +62,30 @@
     private val activity: Activity get() = activityRule.activity
 
     @Test
+    fun first_compose() {
+        benchmarkRule.measureFirstCompose(activity,
+            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+    }
+
+    @Test
+    fun first_measure() {
+        benchmarkRule.measureFirstMeasure(activity,
+            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+    }
+
+    @Test
+    fun first_layout() {
+        benchmarkRule.measureFirstLayout(activity,
+            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+    }
+
+    @Test
+    fun first_draw() {
+        benchmarkRule.measureFirstDraw(activity,
+            RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
+    }
+
+    @Test
     fun toggleRectangleColor_recompose() {
         benchmarkRule.toggleStateMeasureRecompose(activity,
             RectsInColumnSharedModelTestCase(activity, numberOfRectangles))
diff --git a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/view/AndroidCheckboxesInLinearLayoutBenchmark.kt b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/view/AndroidCheckboxesInLinearLayoutBenchmark.kt
index ce982a7..d431b29 100644
--- a/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/view/AndroidCheckboxesInLinearLayoutBenchmark.kt
+++ b/ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/view/AndroidCheckboxesInLinearLayoutBenchmark.kt
@@ -17,10 +17,14 @@
 package androidx.ui.benchmark.test.view
 
 import android.app.Activity
-import androidx.benchmark.BenchmarkRule
+import androidx.benchmark.junit4.BenchmarkRule
 import androidx.test.filters.LargeTest
 import androidx.test.rule.ActivityTestRule
 import androidx.ui.benchmark.measureDrawPerf
+import androidx.ui.benchmark.measureFirstDraw
+import androidx.ui.benchmark.measureFirstLayout
+import androidx.ui.benchmark.measureFirstMeasure
+import androidx.ui.benchmark.measureFirstSetContent
 import androidx.ui.benchmark.measureLayoutPerf
 import androidx.ui.test.DisableTransitions
 import androidx.ui.test.cases.view.AndroidCheckboxesInLinearLayoutTestCase
@@ -51,15 +55,41 @@
     @get:Rule
     val disableAnimationRule = DisableTransitions()
 
+    private val activity: Activity get() = activityRule.activity
+
+    @Test
+    fun first_setContent() {
+        benchmarkRule.measureFirstSetContent(activity,
+            AndroidCheckboxesInLinearLayoutTestCase(activity, numberOfCheckboxes))
+    }
+
+    @Test
+    fun first_measure() {
+        benchmarkRule.measureFirstMeasure(activity,
+            AndroidCheckboxesInLinearLayoutTestCase(activity, numberOfCheckboxes))
+    }
+
+    @Test
+    fun first_layout() {
+        benchmarkRule.measureFirstLayout(activity,
+            AndroidCheckboxesInLinearLayoutTestCase(activity, numberOfCheckboxes))
+    }
+
+    @Test
+    fun first_draw() {
+        benchmarkRule.measureFirstDraw(activity,
+            AndroidCheckboxesInLinearLayoutTestCase(activity, numberOfCheckboxes))
+    }
+
     @Test
     fun layout() {
-        benchmarkRule.measureLayoutPerf(activityRule.activity,
-            AndroidCheckboxesInLinearLayoutTestCase(activityRule.activity, numberOfCheckboxes))
+        benchmarkRule.measureLayoutPerf(activity,
+            AndroidCheckboxesInLinearLayoutTestCase(activity, numberOfCheckboxes))
     }
 
     @Test
     fun draw() {
-        benchmarkRule.measureDrawPerf(activityRule.activity,
-            AndroidCheckboxesInLinearLayoutTestCase(activityRule.activity, numberOfCheckboxes))
+        benchmarkRule.measureDrawPerf(activity,
+            AndroidCheckboxesInLinearLayoutTestCase(activity, numberOfCheckboxes))
     }
 }
\ No newline at end of file
diff --git a/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/BenchmarksExtensions.kt b/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/BenchmarksExtensions.kt
index 7d17561..bc6446b 100644
--- a/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/BenchmarksExtensions.kt
+++ b/ui/integration-tests/benchmark/src/main/java/androidx/ui/benchmark/BenchmarksExtensions.kt
@@ -18,8 +18,10 @@
 
 import android.app.Activity
 import android.view.View
-import androidx.benchmark.BenchmarkRule
-import androidx.benchmark.measureRepeated
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
+import androidx.compose.disposeComposition
+import androidx.ui.test.AndroidTestCase
 import androidx.ui.test.ComposeTestCase
 import androidx.ui.test.TestCase
 import androidx.ui.test.ToggleableTestCase
@@ -30,11 +32,11 @@
 import androidx.ui.test.runOnUiThreadSync
 
 /**
- * Measures measure and layout performance of the given testCase by toggling measure constraints.
+ * Measures measure and layout performance of the given test case by toggling measure constraints.
  */
 fun BenchmarkRule.measureLayoutPerf(activity: Activity, testCase: TestCase) {
     activity.runOnUiThreadSync {
-        testCase.runSetup()
+        testCase.runToFirstDraw()
 
         val width = testCase.view.measuredWidth
         val height = testCase.view.measuredHeight
@@ -66,15 +68,19 @@
             testCase.measureWithSpec(widthSpec, heightSpec)
             testCase.layout()
         }
+
+        if (testCase is ComposeTestCase) {
+            activity.disposeComposition()
+        }
     }
 }
 
 /**
- * Measures draw performance of the given testCase by invalidating the view hierarchy.
+ * Measures draw performance of the given test case by invalidating the view hierarchy.
  */
 fun BenchmarkRule.measureDrawPerf(activity: Activity, testCase: TestCase) {
     activity.runOnUiThreadSync {
-        testCase.runSetup()
+        testCase.runToFirstDraw()
 
         measureRepeated {
             runWithTimingDisabled {
@@ -86,6 +92,125 @@
                 testCase.finishDraw()
             }
         }
+
+        if (testCase is ComposeTestCase) {
+            activity.disposeComposition()
+        }
+    }
+}
+
+/**
+ * Measures the time of the first composition of the given compose test case.
+ */
+fun BenchmarkRule.measureFirstCompose(
+    activity: Activity,
+    testCase: ComposeTestCase
+) {
+    activity.runOnUiThreadSync {
+        measureRepeated {
+            testCase.setupContent(activity)
+            runWithTimingDisabled {
+                testCase.recomposeSyncAssertNoChanges()
+                activity.disposeComposition()
+            }
+        }
+    }
+}
+
+/**
+ * Measures the time of the first set content of the given Android test case.
+ */
+fun BenchmarkRule.measureFirstSetContent(
+    activity: Activity,
+    testCase: AndroidTestCase
+) {
+    activity.runOnUiThreadSync {
+        measureRepeated {
+            testCase.setupContent(activity)
+        }
+    }
+}
+
+/**
+ * Measures the time of the first measure of the given test case.
+ */
+fun BenchmarkRule.measureFirstMeasure(
+    activity: Activity,
+    testCase: TestCase
+) {
+    activity.runOnUiThreadSync {
+        measureRepeated {
+            runWithTimingDisabled {
+                testCase.setupContent(activity)
+                testCase.requestLayout()
+            }
+
+            testCase.measure()
+
+            runWithTimingDisabled {
+                if (testCase is ComposeTestCase) {
+                    testCase.recomposeSyncAssertNoChanges()
+                    activity.disposeComposition()
+                }
+            }
+        }
+    }
+}
+
+/**
+ * Measures the time of the first layout of the given test case.
+ */
+fun BenchmarkRule.measureFirstLayout(
+    activity: Activity,
+    testCase: TestCase
+) {
+    activity.runOnUiThreadSync {
+        measureRepeated {
+            runWithTimingDisabled {
+                testCase.setupContent(activity)
+                testCase.requestLayout()
+                testCase.measure()
+            }
+
+            testCase.layout()
+
+            runWithTimingDisabled {
+                if (testCase is ComposeTestCase) {
+                    testCase.recomposeSyncAssertNoChanges()
+                    activity.disposeComposition()
+                }
+            }
+        }
+    }
+}
+
+/**
+ * Measures the time of the first draw of the given test case.
+ */
+fun BenchmarkRule.measureFirstDraw(
+    activity: Activity,
+    testCase: TestCase
+) {
+    activity.runOnUiThreadSync {
+        measureRepeated {
+            runWithTimingDisabled {
+                testCase.setupContent(activity)
+                testCase.requestLayout()
+                testCase.measure()
+                testCase.layout()
+                testCase.prepareDraw()
+            }
+
+            testCase.draw()
+
+            runWithTimingDisabled {
+                testCase.finishDraw()
+                if (testCase is ComposeTestCase) {
+                    testCase.recomposeSyncAssertNoChanges()
+                    activity.disposeComposition()
+                }
+            }
+        }
     }
 }
 
@@ -96,29 +221,17 @@
     activity: Activity,
     testCase: T
 ) where T : ComposeTestCase, T : ToggleableTestCase {
-    toggleStateMeasureMeasure(activity, testCase) {
-        testCase.toggleState()
-    }
-}
-
-/**
- *  Measures recomposition time of the hierarchy after changing a state.
- */
-fun BenchmarkRule.toggleStateMeasureRecompose(
-    activity: Activity,
-    testCase: ComposeTestCase,
-    toggleState: () -> Unit
-) {
     activity.runOnUiThreadSync {
-        testCase.runSetup()
+        testCase.runToFirstDraw()
         testCase.recomposeSyncAssertNoChanges()
 
         measureRepeated {
             runWithTimingDisabled {
-                toggleState()
+                testCase.toggleState()
             }
             testCase.recomposeSyncAssertHadChanges()
         }
+        activity.disposeComposition()
     }
 }
 
@@ -129,31 +242,19 @@
     activity: Activity,
     testCase: T
 ) where T : ComposeTestCase, T : ToggleableTestCase {
-    toggleStateMeasureMeasure(activity, testCase) {
-        testCase.toggleState()
-    }
-}
-
-/**
- *  Measures measure time of the hierarchy after changing a state.
- */
-fun BenchmarkRule.toggleStateMeasureMeasure(
-    activity: Activity,
-    testCase: ComposeTestCase,
-    toggleState: () -> Unit
-) {
     activity.runOnUiThreadSync {
-        testCase.runSetup()
+        testCase.runToFirstDraw()
         testCase.recomposeSyncAssertNoChanges()
 
         measureRepeated {
             runWithTimingDisabled {
-                toggleState()
+                testCase.toggleState()
                 testCase.recomposeSyncAssertHadChanges()
                 testCase.requestLayout()
             }
             testCase.measure()
         }
+        activity.disposeComposition()
     }
 }
 
@@ -164,32 +265,20 @@
     activity: Activity,
     testCase: T
 ) where T : ComposeTestCase, T : ToggleableTestCase {
-    toggleStateMeasureLayout(activity, testCase) {
-        testCase.toggleState()
-    }
-}
-
-/**
- *  Measures layout time of the hierarchy after changing a state.
- */
-fun BenchmarkRule.toggleStateMeasureLayout(
-    activity: Activity,
-    testCase: ComposeTestCase,
-    toggleState: () -> Unit
-) {
     activity.runOnUiThreadSync {
-        testCase.runSetup()
+        testCase.runToFirstDraw()
         testCase.recomposeSyncAssertNoChanges()
 
         measureRepeated {
             runWithTimingDisabled {
-                toggleState()
+                testCase.toggleState()
                 testCase.recomposeSyncAssertHadChanges()
                 testCase.requestLayout()
                 testCase.measure()
             }
             testCase.layout()
         }
+        activity.disposeComposition()
     }
 }
 
@@ -200,26 +289,13 @@
     activity: Activity,
     testCase: T
 ) where T : ComposeTestCase, T : ToggleableTestCase {
-    toggleStateMeasureDraw(activity, testCase) {
-        testCase.toggleState()
-    }
-}
-
-/**
- *  Measures draw time of the hierarchy after changing a state.
- */
-fun BenchmarkRule.toggleStateMeasureDraw(
-    activity: Activity,
-    testCase: ComposeTestCase,
-    toggleState: () -> Unit
-) {
     activity.runOnUiThreadSync {
-        testCase.runSetup()
+        testCase.runToFirstDraw()
         testCase.recomposeSyncAssertNoChanges()
 
         measureRepeated {
             runWithTimingDisabled {
-                toggleState()
+                testCase.toggleState()
                 testCase.recomposeSyncAssertHadChanges()
                 testCase.requestLayout()
                 testCase.measure()
@@ -231,5 +307,6 @@
                 testCase.finishDraw()
             }
         }
+        activity.disposeComposition()
     }
-}
\ No newline at end of file
+}
diff --git a/ui/integration-tests/demos/build.gradle b/ui/integration-tests/demos/build.gradle
index ca3b08d..c73e7dc 100644
--- a/ui/integration-tests/demos/build.gradle
+++ b/ui/integration-tests/demos/build.gradle
@@ -14,6 +14,7 @@
     implementation(project(":ui:ui-framework:integration-tests:ui-framework-demos"))
     implementation(project(":ui:ui-layout:integration-tests:ui-layout-demos"))
     implementation(project(":ui:ui-material:integration-tests:ui-material-demos"))
+    implementation(project(":ui:ui-foundation:integration-tests:ui-foundation-demos"))
     implementation(project(":ui:ui-text:integration-tests:ui-text-demos"))
     implementation(KOTLIN_COMPOSE_STDLIB)
 
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/TestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/TestCase.kt
index 2103bb1..7004525 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/TestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/TestCase.kt
@@ -41,6 +41,8 @@
     private val renderNode = RenderNode("test")
     private var canvas: Canvas? = null
 
+    var view: ViewGroup
+
     init {
         val displayMetrics = DisplayMetrics()
         activity.windowManager.defaultDisplay.getMetrics(displayMetrics)
@@ -49,11 +51,21 @@
 
         screenWithSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST)
         screenHeightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST)
+        view = activity.findViewById(android.R.id.content)
     }
 
-    lateinit var view: ViewGroup
+    abstract fun setupContent(activity: Activity)
 
-    abstract fun runSetup()
+    /**
+     * Runs all the steps leading into drawing first pixels. Useful to get into the initial state
+     * before you benchmark a change of the state.
+     */
+    fun runToFirstDraw() {
+        setupContent(activity)
+        measure()
+        layout()
+        drawSlow()
+    }
 
     /**
      * To be run in benchmark.
@@ -108,6 +120,10 @@
     }
 }
 
+abstract class AndroidTestCase(
+    activity: Activity
+) : TestCase(activity)
+
 abstract class ComposeTestCase(
     activity: Activity
 ) : TestCase(activity) {
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/TestExecutors.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/TestExecutors.kt
index 55cb4d1..909a8e6 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/TestExecutors.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/TestExecutors.kt
@@ -24,7 +24,7 @@
     testCase: ComposeTestCase,
     toggleState: () -> Unit
 ) {
-    testCase.runSetup()
+    testCase.runToFirstDraw()
 
     testCase.assertMeasureSizeIsPositive()
 
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/CheckboxesInRowsTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/CheckboxesInRowsTestCase.kt
index bb8f07f..fa19488 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/CheckboxesInRowsTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/CheckboxesInRowsTestCase.kt
@@ -31,6 +31,7 @@
 import androidx.ui.layout.FlexRow
 import androidx.ui.material.Checkbox
 import androidx.ui.material.MaterialTheme
+import androidx.ui.material.surface.Surface
 import androidx.ui.test.ComposeTestCase
 import androidx.ui.test.ToggleableTestCase
 
@@ -45,18 +46,20 @@
 
     private val states = mutableListOf<State<Boolean>>()
 
-    override fun runSetup() {
+    override fun setupContent(activity: Activity) {
         compositionContext = activity.setContent {
             MaterialTheme {
-                Column {
-                    repeat(amountOfCheckboxes) {
-                        FlexRow {
-                            inflexible {
-                                Text(text = "Check Me!")
-                            }
-                            expanded(1f) {
-                                Align(alignment = Alignment.CenterRight) {
-                                    CheckboxWithState()
+                Surface {
+                    Column {
+                        repeat(amountOfCheckboxes) {
+                            FlexRow {
+                                inflexible {
+                                    Text(text = "Check Me!")
+                                }
+                                expanded(1f) {
+                                    Align(alignment = Alignment.CenterRight) {
+                                        CheckboxWithState()
+                                    }
                                 }
                             }
                         }
@@ -65,12 +68,6 @@
             }
         }!!
         FrameManager.nextFrame()
-
-        view = activity.findViewById(android.R.id.content)
-
-        measure()
-        layout()
-        drawSlow()
     }
 
     override fun toggleState() {
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt
index d4bb20e..d33c624 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnSharedModelTestCase.kt
@@ -46,7 +46,7 @@
 
     private val model = RectanglesInColumnTestCaseColorModel(Color.Black)
 
-    override fun runSetup() {
+    override fun setupContent(activity: Activity) {
         compositionContext = activity.setContent {
             MaterialTheme {
                 Column {
@@ -61,12 +61,6 @@
             }
         }!!
         FrameManager.nextFrame()
-
-        view = activity.findViewById(android.R.id.content)
-
-        measure()
-        layout()
-        drawSlow()
     }
 
     override fun toggleState() {
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnTestCase.kt
index 66c9793..526c984 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/RectsInColumnTestCase.kt
@@ -29,6 +29,7 @@
 import androidx.ui.graphics.Color
 import androidx.ui.layout.Column
 import androidx.ui.material.MaterialTheme
+import androidx.ui.material.surface.Surface
 import androidx.ui.test.ComposeTestCase
 import androidx.ui.test.ToggleableTestCase
 
@@ -45,23 +46,19 @@
 
     private val states = mutableListOf<State<Color>>()
 
-    override fun runSetup() {
+    override fun setupContent(activity: Activity) {
         compositionContext = activity.setContent {
             MaterialTheme {
-                Column {
-                    repeat(amountOfRectangles) {
-                        ColoredRectWithModel()
+                Surface {
+                    Column {
+                        repeat(amountOfRectangles) {
+                            ColoredRectWithModel()
+                        }
                     }
                 }
             }
         }!!
         FrameManager.nextFrame()
-
-        view = activity.findViewById(android.R.id.content)
-
-        measure()
-        layout()
-        drawSlow()
     }
 
     override fun toggleState() {
diff --git a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidCheckboxesInLinearLayoutTestCase.kt b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidCheckboxesInLinearLayoutTestCase.kt
index e12d6fc..1d93e85 100644
--- a/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidCheckboxesInLinearLayoutTestCase.kt
+++ b/ui/integration-tests/test/src/main/java/androidx/ui/test/cases/view/AndroidCheckboxesInLinearLayoutTestCase.kt
@@ -22,6 +22,7 @@
 import android.widget.CheckBox
 import android.widget.LinearLayout
 import android.widget.TextView
+import androidx.ui.test.AndroidTestCase
 import androidx.ui.test.R
 import androidx.ui.test.TestCase
 
@@ -31,11 +32,11 @@
 class AndroidCheckboxesInLinearLayoutTestCase(
     activity: Activity,
     private val amountOfCheckboxes: Int
-) : TestCase(activity) {
+) : AndroidTestCase(activity) {
 
     private val checkboxes = mutableListOf<CheckBox>()
 
-    override fun runSetup() {
+    override fun setupContent(activity: Activity) {
         val column = LinearLayout(activity)
         column.orientation = LinearLayout.VERTICAL
         column.layoutParams = ViewGroup.LayoutParams(
@@ -72,13 +73,7 @@
             row.addView(checkbox)
             column.addView(row)
         }
-
-        view = column
         activity.setContentView(column)
-
-        measure()
-        layout()
-        drawSlow()
     }
 
     fun toggleState() {
diff --git a/ui/settings.gradle b/ui/settings.gradle
index c46fc60..75431ad 100644
--- a/ui/settings.gradle
+++ b/ui/settings.gradle
@@ -20,7 +20,8 @@
 }
 
 includeProject(":annotation:annotation-sampled", "../annotation/annotation-sampled")
-includeProject(":benchmark", "../benchmark")
+includeProject(":benchmark:benchmark-common", "../benchmark/common")
+includeProject(":benchmark:benchmark-junit4", "../benchmark/junit4")
 includeProject(":compose:compose-compiler", "../compose/compose-compiler")
 includeProject(":compose:compose-compiler-hosted", "../compose/compose-compiler-hosted")
 includeProject(":compose:compose-compiler-hosted:integration-tests", "../compose/compose-compiler-hosted/integration-tests")
@@ -39,6 +40,7 @@
 includeProject(":ui:ui-core", "ui-core")
 includeProject(":ui:ui-foundation", "ui-foundation")
 includeProject(":ui:ui-foundation:integration-tests:samples", "ui-foundation/integration-tests/samples")
+includeProject(":ui:ui-foundation:integration-tests:ui-foundation-demos", "ui-foundation/integration-tests/foundation-demos")
 includeProject(":ui:ui-framework", "ui-framework")
 includeProject(":ui:ui-framework:integration-tests:ui-framework-demos", "ui-framework/integration-tests/framework-demos")
 includeProject(":ui:ui-framework:integration-tests:samples", "ui-framework/integration-tests/samples")
diff --git a/ui/ui-animation/integration-tests/animation-demos/build.gradle b/ui/ui-animation/integration-tests/animation-demos/build.gradle
index f4a54b0..beaca6b 100644
--- a/ui/ui-animation/integration-tests/animation-demos/build.gradle
+++ b/ui/ui-animation/integration-tests/animation-demos/build.gradle
@@ -29,7 +29,7 @@
 }
 
 androidx {
-    name = "Crane Animation Composables"
+    name = "Compose Animation Composables"
     publish = Publish.SNAPSHOT_AND_RELEASE
     mavenVersion = LibraryVersions.UI
     mavenGroup = LibraryGroups.UI
diff --git a/ui/ui-core/api/1.0.0-alpha01.txt b/ui/ui-core/api/1.0.0-alpha01.txt
index 88194bc..d0525d8 100644
--- a/ui/ui-core/api/1.0.0-alpha01.txt
+++ b/ui/ui-core/api/1.0.0-alpha01.txt
@@ -1669,6 +1669,15 @@
 
 }
 
+package androidx.ui.temputils {
+
+  public final class CoroutineUtilsKt {
+    ctor public CoroutineUtilsKt();
+    method public static kotlinx.coroutines.Job delay(androidx.ui.core.Duration duration, kotlin.coroutines.CoroutineContext context, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
+}
+
 package androidx.ui.testutils {
 
   public final class PointerInputTestUtilKt {
diff --git a/ui/ui-core/api/current.txt b/ui/ui-core/api/current.txt
index 88194bc..d0525d8 100644
--- a/ui/ui-core/api/current.txt
+++ b/ui/ui-core/api/current.txt
@@ -1669,6 +1669,15 @@
 
 }
 
+package androidx.ui.temputils {
+
+  public final class CoroutineUtilsKt {
+    ctor public CoroutineUtilsKt();
+    method public static kotlinx.coroutines.Job delay(androidx.ui.core.Duration duration, kotlin.coroutines.CoroutineContext context, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
+}
+
 package androidx.ui.testutils {
 
   public final class PointerInputTestUtilKt {
diff --git a/ui/ui-core/api/restricted_1.0.0-alpha01.txt b/ui/ui-core/api/restricted_1.0.0-alpha01.txt
index 88194bc..d0525d8 100644
--- a/ui/ui-core/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-core/api/restricted_1.0.0-alpha01.txt
@@ -1669,6 +1669,15 @@
 
 }
 
+package androidx.ui.temputils {
+
+  public final class CoroutineUtilsKt {
+    ctor public CoroutineUtilsKt();
+    method public static kotlinx.coroutines.Job delay(androidx.ui.core.Duration duration, kotlin.coroutines.CoroutineContext context, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
+}
+
 package androidx.ui.testutils {
 
   public final class PointerInputTestUtilKt {
diff --git a/ui/ui-core/api/restricted_current.txt b/ui/ui-core/api/restricted_current.txt
index 88194bc..d0525d8 100644
--- a/ui/ui-core/api/restricted_current.txt
+++ b/ui/ui-core/api/restricted_current.txt
@@ -1669,6 +1669,15 @@
 
 }
 
+package androidx.ui.temputils {
+
+  public final class CoroutineUtilsKt {
+    ctor public CoroutineUtilsKt();
+    method public static kotlinx.coroutines.Job delay(androidx.ui.core.Duration duration, kotlin.coroutines.CoroutineContext context, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
+}
+
 package androidx.ui.testutils {
 
   public final class PointerInputTestUtilKt {
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Dp.kt b/ui/ui-core/src/main/java/androidx/ui/core/Dp.kt
index 8168130..e7981fe 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/Dp.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Dp.kt
@@ -17,6 +17,7 @@
 
 package androidx.ui.core
 
+import androidx.compose.Immutable
 import androidx.ui.core.Dp.Companion.Hairline
 import androidx.ui.lerp
 import kotlin.math.max
@@ -37,6 +38,7 @@
  * [toPx] is normally needed only for painting operations.
  */
 @Suppress("EXPERIMENTAL_FEATURE_WARNING")
+@Immutable
 data /*inline*/ class Dp(val value: Float) {
     /**
      * Add two [Dp]s together.
@@ -222,6 +224,7 @@
  *     val width = oldWidth * newTotalWidth / oldTotalWidth
  */
 @Suppress("EXPERIMENTAL_FEATURE_WARNING")
+@Immutable
 inline class DpSquared(val value: Float) {
     /**
      * Add two DimensionSquares together.
@@ -287,6 +290,7 @@
  *     val width = oldWidth * newTotalWidth / oldTotalWidth
  */
 @Suppress("EXPERIMENTAL_FEATURE_WARNING")
+@Immutable
 inline class DpCubed(val value: Float) {
 
     /**
@@ -345,6 +349,7 @@
  *     val width = oldWidth * newTotalWidth / oldTotalWidth
  */
 @Suppress("EXPERIMENTAL_FEATURE_WARNING")
+@Immutable
 inline class DpInverse(val value: Float) {
     /**
      * Add two DpInverse together.
@@ -401,6 +406,7 @@
 /**
  * A two dimensional size using [Dp] for units
  */
+@Immutable
 data class Size(val width: Dp, val height: Dp)
 
 /**
@@ -414,6 +420,7 @@
 /**
  * A two-dimensional position using [Dp] for units
  */
+@Immutable
 data class Position(val x: Dp, val y: Dp) {
     /**
      * Subtract a [Position] from another one.
@@ -452,6 +459,7 @@
 /**
  * A four dimensional bounds using [Dp] for units
  */
+@Immutable
 data class Bounds(
     val left: Dp,
     val top: Dp,
diff --git a/ui/ui-core/src/main/java/androidx/ui/graphics/Color.kt b/ui/ui-core/src/main/java/androidx/ui/graphics/Color.kt
index 73765cd..9644d21 100644
--- a/ui/ui-core/src/main/java/androidx/ui/graphics/Color.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/graphics/Color.kt
@@ -20,6 +20,7 @@
 import androidx.annotation.FloatRange
 import androidx.annotation.IntRange
 import androidx.annotation.Size
+import androidx.compose.Immutable
 import androidx.ui.lerp
 import androidx.ui.util.Float16
 import kotlin.math.max
@@ -112,6 +113,7 @@
  */
 @UseExperimental(kotlin.ExperimentalUnsignedTypes::class)
 @AnyThread
+@Immutable
 class Color constructor(private val value: ULong) {
     /**
      * Returns this color's color space.
diff --git a/ui/ui-core/src/main/java/androidx/ui/temputils/CoroutineUtils.kt b/ui/ui-core/src/main/java/androidx/ui/temputils/CoroutineUtils.kt
new file mode 100644
index 0000000..a0bfc403
--- /dev/null
+++ b/ui/ui-core/src/main/java/androidx/ui/temputils/CoroutineUtils.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.temputils
+
+import androidx.ui.core.Duration
+import androidx.ui.core.inMilliseconds
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlin.coroutines.CoroutineContext
+
+/*
+ * This file is a temporary place to utilize coroutines before they are work in the IR compiler.
+ */
+
+/**
+ * Run [block] after [duration] time passes using [context].
+ *
+ * @return [Job] which is a reference to the running coroutine such that it can be cancelled via [Job.cancel].
+ */
+fun delay(duration: Duration, context: CoroutineContext, block: () -> Unit) =
+    CoroutineScope(context).launch {
+        kotlinx.coroutines.delay(duration.inMilliseconds())
+        block()
+    }
\ No newline at end of file
diff --git a/ui/ui-foundation/api/1.0.0-alpha01.txt b/ui/ui-foundation/api/1.0.0-alpha01.txt
index 26ee3a8..5edc08e 100644
--- a/ui/ui-foundation/api/1.0.0-alpha01.txt
+++ b/ui/ui-foundation/api/1.0.0-alpha01.txt
@@ -17,6 +17,11 @@
     method public static void DeterminateProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
+  public final class DialogKt {
+    ctor public DialogKt();
+    method public static void Dialog(kotlin.jvm.functions.Function0<kotlin.Unit> onCloseRequest, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+  }
+
   public final class SimpleImageKt {
     ctor public SimpleImageKt();
     method public static void SimpleImage(androidx.ui.painting.Image image, androidx.ui.graphics.Color? tint = null);
diff --git a/ui/ui-foundation/api/current.txt b/ui/ui-foundation/api/current.txt
index 26ee3a8..5edc08e 100644
--- a/ui/ui-foundation/api/current.txt
+++ b/ui/ui-foundation/api/current.txt
@@ -17,6 +17,11 @@
     method public static void DeterminateProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
+  public final class DialogKt {
+    ctor public DialogKt();
+    method public static void Dialog(kotlin.jvm.functions.Function0<kotlin.Unit> onCloseRequest, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+  }
+
   public final class SimpleImageKt {
     ctor public SimpleImageKt();
     method public static void SimpleImage(androidx.ui.painting.Image image, androidx.ui.graphics.Color? tint = null);
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 26ee3a8..5edc08e 100644
--- a/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-foundation/api/restricted_1.0.0-alpha01.txt
@@ -17,6 +17,11 @@
     method public static void DeterminateProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
+  public final class DialogKt {
+    ctor public DialogKt();
+    method public static void Dialog(kotlin.jvm.functions.Function0<kotlin.Unit> onCloseRequest, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+  }
+
   public final class SimpleImageKt {
     ctor public SimpleImageKt();
     method public static void SimpleImage(androidx.ui.painting.Image image, androidx.ui.graphics.Color? tint = null);
diff --git a/ui/ui-foundation/api/restricted_current.txt b/ui/ui-foundation/api/restricted_current.txt
index 26ee3a8..5edc08e 100644
--- a/ui/ui-foundation/api/restricted_current.txt
+++ b/ui/ui-foundation/api/restricted_current.txt
@@ -17,6 +17,11 @@
     method public static void DeterminateProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
+  public final class DialogKt {
+    ctor public DialogKt();
+    method public static void Dialog(kotlin.jvm.functions.Function0<kotlin.Unit> onCloseRequest, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+  }
+
   public final class SimpleImageKt {
     ctor public SimpleImageKt();
     method public static void SimpleImage(androidx.ui.painting.Image image, androidx.ui.graphics.Color? tint = null);
diff --git a/ui/ui-foundation/build.gradle b/ui/ui-foundation/build.gradle
index e2568bd..105aab3 100644
--- a/ui/ui-foundation/build.gradle
+++ b/ui/ui-foundation/build.gradle
@@ -40,6 +40,7 @@
     implementation project(":ui:ui-animation")
     implementation project(':ui:ui-framework')
     implementation project(':ui:ui-layout')
+    implementation project(':ui:ui-platform')
     implementation project(':ui:ui-text')
 
     testImplementation(ANDROIDX_TEST_RULES)
@@ -48,6 +49,7 @@
 
     androidTestImplementation project(':ui:ui-test')
 
+    androidTestImplementation(ANDROIDX_TEST_UIAUTOMATOR)
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(JUNIT)
diff --git a/ui/ui-foundation/integration-tests/foundation-demos/build.gradle b/ui/ui-foundation/integration-tests/foundation-demos/build.gradle
new file mode 100644
index 0000000..c46ed89
--- /dev/null
+++ b/ui/ui-foundation/integration-tests/foundation-demos/build.gradle
@@ -0,0 +1,66 @@
+/*
+ * 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 org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("AndroidXUiPlugin")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
+
+    implementation(KOTLIN_COMPOSE_COROUTINES)
+    implementation(KOTLIN_COMPOSE_STDLIB)
+
+    implementation "androidx.activity:activity:1.0.0-alpha01"
+    implementation "androidx.annotation:annotation:1.1.0"
+
+    implementation project(":compose:compose-runtime")
+    implementation project(":ui:ui-core")
+    implementation project(":ui:ui-foundation")
+    implementation project(":ui:ui-framework")
+    implementation project(":ui:ui-animation")
+    implementation project(":ui:ui-layout")
+    implementation project(":ui:ui-foundation:integration-tests:samples")
+    implementation project(":ui:ui-text")
+    implementation project(':ui:ui-android-view-non-ir')
+}
+
+android {
+    tasks.withType(KotlinCompile).configureEach {
+        kotlinOptions {
+            useIR = true
+        }
+    }
+}
+
+androidx {
+    name = "Compose Foundation Demos"
+    publish = Publish.NONE
+    mavenVersion = LibraryVersions.UI
+    mavenGroup = LibraryGroups.UI
+    inceptionYear = "2019"
+    description = "This is a project for Foundation demos."
+}
diff --git a/benchmark/src/androidTest/AndroidManifest.xml b/ui/ui-foundation/integration-tests/foundation-demos/src/main/AndroidManifest.xml
similarity index 60%
copy from benchmark/src/androidTest/AndroidManifest.xml
copy to ui/ui-foundation/integration-tests/foundation-demos/src/main/AndroidManifest.xml
index f5ec776..5cb729b 100644
--- a/benchmark/src/androidTest/AndroidManifest.xml
+++ b/ui/ui-foundation/integration-tests/foundation-demos/src/main/AndroidManifest.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
   Copyright 2019 The Android Open Source Project
 
@@ -14,13 +13,20 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
+
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="androidx.benchmark.test">
+    package="androidx.ui.foundation.demos">
 
-    <application
-        android:name="androidx.benchmark.ArgumentInjectingApplication">
-        <activity android:name="android.app.Activity"/>
+    <application>
+
+        <activity android:name=".AnimatedDraggableActivity"
+            android:configChanges="orientation|screenSize"
+            android:label="Foundation/AnimatedDraggable">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="androidx.ui.demos.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
     </application>
-</manifest>
\ No newline at end of file
+</manifest>
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
new file mode 100644
index 0000000..d33545a
--- /dev/null
+++ b/ui/ui-foundation/integration-tests/foundation-demos/src/main/java/androidx/ui/foundation/demos/AnimatedDraggableActivity.kt
@@ -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.
+ */
+
+package androidx.ui.foundation.demos
+
+import android.app.Activity
+import android.os.Bundle
+import androidx.compose.composer
+import androidx.ui.core.setContent
+import androidx.ui.foundation.samples.AnimatedDraggableSample
+import androidx.ui.layout.Wrap
+
+class AnimatedDraggableActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent {
+            Wrap {
+                AnimatedDraggableSample()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-foundation/integration-tests/samples/build.gradle b/ui/ui-foundation/integration-tests/samples/build.gradle
index 446e4b2..beb8562 100644
--- a/ui/ui-foundation/integration-tests/samples/build.gradle
+++ b/ui/ui-foundation/integration-tests/samples/build.gradle
@@ -34,6 +34,7 @@
 
     implementation project(":compose:compose-runtime")
     implementation project(":ui:ui-core")
+    implementation project(":ui:ui-animation")
     implementation project(":ui:ui-foundation")
     implementation project(":ui:ui-framework")
     implementation project(":ui:ui-layout")
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
new file mode 100644
index 0000000..3714185
--- /dev/null
+++ b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/AnimatedDraggableSamples.kt
@@ -0,0 +1,72 @@
+/*
+ * 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/DialogSample.kt b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/DialogSample.kt
new file mode 100644
index 0000000..2c01062c
--- /dev/null
+++ b/ui/ui-foundation/integration-tests/samples/src/main/java/androidx/ui/foundation/samples/DialogSample.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.state
+import androidx.compose.unaryPlus
+import androidx.ui.core.Text
+import androidx.ui.foundation.Dialog
+
+@Sampled
+@Composable
+fun DialogSample() {
+    val openDialog = +state { true }
+
+    if (openDialog.value) {
+        Dialog(onCloseRequest = { openDialog.value = false }) {
+            Text("This is a Dialog. Click outside to dismiss.")
+        }
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/DialogUiTest.kt b/ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/DialogUiTest.kt
new file mode 100644
index 0000000..0bfd0af
--- /dev/null
+++ b/ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/DialogUiTest.kt
@@ -0,0 +1,172 @@
+/*
+ * 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
+
+import androidx.test.filters.MediumTest
+import androidx.ui.test.createComposeRule
+import androidx.compose.composer
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.uiautomator.UiDevice
+import androidx.ui.core.Text
+import androidx.ui.test.assertDoesNotExist
+import androidx.ui.test.assertIsVisible
+import androidx.ui.test.doClick
+import androidx.ui.test.findByText
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@MediumTest
+@RunWith(JUnit4::class)
+class DialogUiTest {
+    @get:Rule
+    val composeTestRule = createComposeRule(disableTransitions = true)
+
+    private val defaultText = "dialogText"
+
+    @Test
+    fun dialogTest_isShowingContent() {
+        composeTestRule.setContent {
+            val showDialog = +state { true }
+
+            if (showDialog.value) {
+                Dialog(onCloseRequest = {}) {
+                    Text(defaultText)
+                }
+            }
+        }
+
+        findByText(defaultText).assertIsVisible()
+    }
+
+    @Test
+    fun dialogTest_isNotDismissed_whenClicked() {
+        val textBeforeClick = "textBeforeClick"
+        val textAfterClick = "textAfterClick"
+
+        composeTestRule.setContent {
+            val showDialog = +state { true }
+            val text = +state { textBeforeClick }
+
+            if (showDialog.value) {
+                Dialog(onCloseRequest = {
+                    showDialog.value = false
+                }) {
+                    Clickable(onClick = { text.value = textAfterClick }) {
+                        Text(text = text.value)
+                    }
+                }
+            }
+        }
+
+        findByText(textBeforeClick).assertIsVisible()
+
+        // Click inside the dialog
+        findByText(textBeforeClick).doClick()
+
+        // Check that the Clickable was pressed and that the Dialog is still visible
+        findByText(textAfterClick).assertIsVisible()
+    }
+
+    @Test
+    fun dialogTest_isDismissed_whenSpecified() {
+        composeTestRule.setContent {
+            val showDialog = +state { true }
+
+            if (showDialog.value) {
+                Dialog(onCloseRequest = { showDialog.value = false }) {
+                    Text(defaultText)
+                }
+            }
+        }
+
+        findByText(defaultText).assertIsVisible()
+
+        // Click outside the dialog to dismiss it
+        val outsideX = 0
+        val outsideY = composeTestRule.displayMetrics.heightPixels / 2
+        UiDevice.getInstance(getInstrumentation()).click(outsideX, outsideY)
+
+        assertDoesNotExist { label.equals(defaultText) }
+    }
+
+    @Test
+    fun dialogTest_isNotDismissed_whenNotSpecified() {
+        composeTestRule.setContent {
+            val showDialog = +state { true }
+
+            if (showDialog.value) {
+                Dialog(onCloseRequest = {}) {
+                    Text(defaultText)
+                }
+            }
+        }
+
+        findByText(defaultText).assertIsVisible()
+
+        // Click outside the dialog to try to dismiss it
+        val outsideX = 0
+        val outsideY = composeTestRule.displayMetrics.heightPixels / 2
+        UiDevice.getInstance(getInstrumentation()).click(outsideX, outsideY)
+
+        // The Dialog should still be visible
+        findByText(defaultText).assertIsVisible()
+    }
+
+    @Test
+    fun dialogTest_isDismissed_whenSpecified_backButtonPressed() {
+        composeTestRule.setContent {
+            val showDialog = +state { true }
+
+            if (showDialog.value) {
+                Dialog(onCloseRequest = { showDialog.value = false }) {
+                    Text(defaultText)
+                }
+            }
+        }
+
+        findByText(defaultText).assertIsVisible()
+
+        // Click the back button to dismiss the Dialog
+        UiDevice.getInstance(getInstrumentation()).pressBack()
+
+        assertDoesNotExist { label.equals(defaultText) }
+    }
+
+    @Test
+    fun dialogTest_isNotDismissed_whenNotSpecified_backButtonPressed() {
+        composeTestRule.setContent {
+            val showDialog = +state { true }
+
+            if (showDialog.value) {
+                Dialog(onCloseRequest = {}) {
+                    Text(defaultText)
+                }
+            }
+        }
+
+        findByText(defaultText).assertIsVisible()
+
+        // Click the back button to try to dismiss the dialog
+        UiDevice.getInstance(getInstrumentation()).pressBack()
+
+        // The Dialog should still be visible
+        findByText(defaultText).assertIsVisible()
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..8ccc5b6
--- /dev/null
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt
@@ -0,0 +1,100 @@
+/*
+ * 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
+
+import android.app.Dialog
+import android.content.Context
+import android.view.MotionEvent
+import android.widget.FrameLayout
+import androidx.compose.Children
+import androidx.compose.Composable
+import androidx.compose.ambient
+import androidx.compose.disposeComposition
+import androidx.compose.memo
+import androidx.compose.onActive
+import androidx.compose.onCommit
+import androidx.compose.onDispose
+import androidx.compose.unaryPlus
+import androidx.ui.core.ContextAmbient
+import androidx.ui.core.setContent
+
+/**
+ * Opens a dialog with the given content.
+ *
+ * The dialog is visible as long as it is part of the composition hierarchy.
+ * In order to let the user dismiss the Dialog, the implementation of [onCloseRequest] should
+ * contain a way to remove to remove the dialog from the composition hierarchy.
+ *
+ * Example usage:
+ *
+ * @sample androidx.ui.foundation.samples.DialogSample
+ *
+ * @param onCloseRequest Executes when the user tries to dismiss the Dialog.
+ * @param children The content to be displayed inside the dialog.
+ */
+@Composable
+fun Dialog(onCloseRequest: () -> Unit, @Children children: @Composable() () -> Unit) {
+    val context = +ambient(ContextAmbient)
+
+    val dialog = +memo { DialogWrapper(context, onCloseRequest) }
+
+    +onActive {
+        dialog.show()
+
+        onDispose {
+            dialog.dismiss()
+            dialog.disposeComposition()
+        }
+    }
+
+    +onCommit {
+        dialog.setContent(children)
+    }
+}
+
+private class DialogWrapper(context: Context, val onCloseRequest: () -> Unit) : Dialog(context) {
+    val frameLayout = FrameLayout(context)
+    init {
+        setContentView(frameLayout)
+    }
+
+    fun setContent(@Children children: @Composable() () -> Unit) {
+        frameLayout.setContent(children)
+    }
+
+    fun disposeComposition() {
+        frameLayout.disposeComposition()
+    }
+
+    override fun onTouchEvent(event: MotionEvent): Boolean {
+        val result = super.onTouchEvent(event)
+        if (result) {
+            onCloseRequest()
+        }
+
+        return result
+    }
+
+    override fun cancel() {
+        // Prevents the dialog from dismissing itself
+        return
+    }
+
+    override fun onBackPressed() {
+        onCloseRequest()
+    }
+}
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
index a429d5b..39e47e1 100644
--- 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
@@ -46,7 +46,7 @@
  *
  * If you need only animations without gesture support, consider using [animatedFloat] instead.
  *
- * //TODO: Add sample
+ * @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
diff --git a/ui/ui-framework/api/1.0.0-alpha01.txt b/ui/ui-framework/api/1.0.0-alpha01.txt
index 7e4b05c..c7820fa 100644
--- a/ui/ui-framework/api/1.0.0-alpha01.txt
+++ b/ui/ui-framework/api/1.0.0-alpha01.txt
@@ -34,7 +34,7 @@
 
   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 = {});
+    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 {
@@ -92,6 +92,11 @@
     property public abstract Object? parentData;
   }
 
+  public interface OffsetMap {
+    method public int originalToTransformed(int offset);
+    method public int transformedToOriginal(int offset);
+  }
+
   public final class OpacityKt {
     ctor public OpacityKt();
     method public static void Opacity(@FloatRange(from=0.0, to=1.0) float opacity, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -102,6 +107,13 @@
     method public static void ParentData(Object data, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
+  public final class PasswordVisualTransformation implements androidx.ui.core.VisualTransformation {
+    ctor public PasswordVisualTransformation(char mask);
+    ctor public PasswordVisualTransformation();
+    method public androidx.ui.core.TransformedText filter(androidx.ui.text.AnnotatedString text);
+    method public char getMask();
+  }
+
   public abstract class Placeable {
     ctor public Placeable();
     method public abstract androidx.ui.core.IntPx getHeight();
@@ -174,12 +186,30 @@
     method public androidx.ui.core.TextSpanComposition getComposer();
   }
 
+  public final class TransformedText {
+    ctor public TransformedText(androidx.ui.text.AnnotatedString transformedText, androidx.ui.core.OffsetMap offsetMap);
+    method public androidx.ui.text.AnnotatedString component1();
+    method public androidx.ui.core.OffsetMap component2();
+    method public androidx.ui.core.TransformedText copy(androidx.ui.text.AnnotatedString transformedText, androidx.ui.core.OffsetMap offsetMap);
+    method public androidx.ui.core.OffsetMap getOffsetMap();
+    method public androidx.ui.text.AnnotatedString getTransformedText();
+  }
+
+  public interface VisualTransformation {
+    method public androidx.ui.core.TransformedText filter(androidx.ui.text.AnnotatedString text);
+  }
+
+  public final class VisualTransformationKt {
+    ctor public VisualTransformationKt();
+  }
+
   public final class WrapperKt {
     ctor public WrapperKt();
     method public static void ComposeView(kotlin.jvm.functions.Function0<kotlin.Unit> children);
     method public static void WithDensity(kotlin.jvm.functions.Function1<? super androidx.ui.core.DensityReceiver,kotlin.Unit> block);
     method @CheckResult(suggest="+") public static androidx.compose.Effect<androidx.ui.core.Density> ambientDensity();
     method public static androidx.compose.Ambient<android.content.Context> getContextAmbient();
+    method public static androidx.compose.Ambient<kotlin.coroutines.CoroutineContext> getCoroutineContextAmbient();
     method public static androidx.compose.Ambient<androidx.ui.core.Density> getDensityAmbient();
     method public static androidx.compose.CompositionContext? setContent(android.app.Activity, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method public static androidx.compose.CompositionContext? setContent(android.view.ViewGroup, kotlin.jvm.functions.Function0<kotlin.Unit> content);
@@ -222,6 +252,11 @@
     method public default void onStop(androidx.ui.core.PxPosition velocity);
   }
 
+  public final class LongPressGestureDetectorKt {
+    ctor public LongPressGestureDetectorKt();
+    method public static void LongPressGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.PxPosition,kotlin.Unit> onLongPress, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+  }
+
   public final class PressGestureDetectorKt {
     ctor public PressGestureDetectorKt();
     method public static void PressGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.PxPosition,kotlin.Unit>? onPress = null, kotlin.jvm.functions.Function0<kotlin.Unit>? onRelease = null, kotlin.jvm.functions.Function0<kotlin.Unit>? onCancel = null, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -261,16 +296,16 @@
 package androidx.ui.core.selection {
 
   public final class Selection {
-    ctor public Selection(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
-    method public androidx.ui.engine.geometry.Rect component1();
-    method public androidx.ui.engine.geometry.Rect component2();
+    ctor public Selection(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+    method public androidx.ui.core.PxPosition component1();
+    method public androidx.ui.core.PxPosition component2();
     method public androidx.ui.core.LayoutCoordinates? component3();
     method public androidx.ui.core.LayoutCoordinates? component4();
-    method public androidx.ui.core.selection.Selection copy(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+    method public androidx.ui.core.selection.Selection copy(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+    method public androidx.ui.core.PxPosition getEndCoordinates();
     method public androidx.ui.core.LayoutCoordinates? getEndLayoutCoordinates();
-    method public androidx.ui.engine.geometry.Rect getEndOffset();
+    method public androidx.ui.core.PxPosition getStartCoordinates();
     method public androidx.ui.core.LayoutCoordinates? getStartLayoutCoordinates();
-    method public androidx.ui.engine.geometry.Rect getStartOffset();
   }
 
   public final class SelectionContainerKt {
@@ -278,6 +313,10 @@
     method public static void SelectionContainer(androidx.ui.core.selection.Selection? selection, kotlin.jvm.functions.Function1<? super androidx.ui.core.selection.Selection,kotlin.Unit> onSelectionChange, androidx.ui.core.selection.SelectionMode mode = SelectionMode.Vertical, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
+  public final class SelectionHandlesKt {
+    ctor public SelectionHandlesKt();
+  }
+
   public final class SelectionKt {
     ctor public SelectionKt();
   }
diff --git a/ui/ui-framework/api/current.txt b/ui/ui-framework/api/current.txt
index 7e4b05c..c7820fa 100644
--- a/ui/ui-framework/api/current.txt
+++ b/ui/ui-framework/api/current.txt
@@ -34,7 +34,7 @@
 
   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 = {});
+    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 {
@@ -92,6 +92,11 @@
     property public abstract Object? parentData;
   }
 
+  public interface OffsetMap {
+    method public int originalToTransformed(int offset);
+    method public int transformedToOriginal(int offset);
+  }
+
   public final class OpacityKt {
     ctor public OpacityKt();
     method public static void Opacity(@FloatRange(from=0.0, to=1.0) float opacity, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -102,6 +107,13 @@
     method public static void ParentData(Object data, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
+  public final class PasswordVisualTransformation implements androidx.ui.core.VisualTransformation {
+    ctor public PasswordVisualTransformation(char mask);
+    ctor public PasswordVisualTransformation();
+    method public androidx.ui.core.TransformedText filter(androidx.ui.text.AnnotatedString text);
+    method public char getMask();
+  }
+
   public abstract class Placeable {
     ctor public Placeable();
     method public abstract androidx.ui.core.IntPx getHeight();
@@ -174,12 +186,30 @@
     method public androidx.ui.core.TextSpanComposition getComposer();
   }
 
+  public final class TransformedText {
+    ctor public TransformedText(androidx.ui.text.AnnotatedString transformedText, androidx.ui.core.OffsetMap offsetMap);
+    method public androidx.ui.text.AnnotatedString component1();
+    method public androidx.ui.core.OffsetMap component2();
+    method public androidx.ui.core.TransformedText copy(androidx.ui.text.AnnotatedString transformedText, androidx.ui.core.OffsetMap offsetMap);
+    method public androidx.ui.core.OffsetMap getOffsetMap();
+    method public androidx.ui.text.AnnotatedString getTransformedText();
+  }
+
+  public interface VisualTransformation {
+    method public androidx.ui.core.TransformedText filter(androidx.ui.text.AnnotatedString text);
+  }
+
+  public final class VisualTransformationKt {
+    ctor public VisualTransformationKt();
+  }
+
   public final class WrapperKt {
     ctor public WrapperKt();
     method public static void ComposeView(kotlin.jvm.functions.Function0<kotlin.Unit> children);
     method public static void WithDensity(kotlin.jvm.functions.Function1<? super androidx.ui.core.DensityReceiver,kotlin.Unit> block);
     method @CheckResult(suggest="+") public static androidx.compose.Effect<androidx.ui.core.Density> ambientDensity();
     method public static androidx.compose.Ambient<android.content.Context> getContextAmbient();
+    method public static androidx.compose.Ambient<kotlin.coroutines.CoroutineContext> getCoroutineContextAmbient();
     method public static androidx.compose.Ambient<androidx.ui.core.Density> getDensityAmbient();
     method public static androidx.compose.CompositionContext? setContent(android.app.Activity, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method public static androidx.compose.CompositionContext? setContent(android.view.ViewGroup, kotlin.jvm.functions.Function0<kotlin.Unit> content);
@@ -222,6 +252,11 @@
     method public default void onStop(androidx.ui.core.PxPosition velocity);
   }
 
+  public final class LongPressGestureDetectorKt {
+    ctor public LongPressGestureDetectorKt();
+    method public static void LongPressGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.PxPosition,kotlin.Unit> onLongPress, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+  }
+
   public final class PressGestureDetectorKt {
     ctor public PressGestureDetectorKt();
     method public static void PressGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.PxPosition,kotlin.Unit>? onPress = null, kotlin.jvm.functions.Function0<kotlin.Unit>? onRelease = null, kotlin.jvm.functions.Function0<kotlin.Unit>? onCancel = null, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -261,16 +296,16 @@
 package androidx.ui.core.selection {
 
   public final class Selection {
-    ctor public Selection(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
-    method public androidx.ui.engine.geometry.Rect component1();
-    method public androidx.ui.engine.geometry.Rect component2();
+    ctor public Selection(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+    method public androidx.ui.core.PxPosition component1();
+    method public androidx.ui.core.PxPosition component2();
     method public androidx.ui.core.LayoutCoordinates? component3();
     method public androidx.ui.core.LayoutCoordinates? component4();
-    method public androidx.ui.core.selection.Selection copy(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+    method public androidx.ui.core.selection.Selection copy(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+    method public androidx.ui.core.PxPosition getEndCoordinates();
     method public androidx.ui.core.LayoutCoordinates? getEndLayoutCoordinates();
-    method public androidx.ui.engine.geometry.Rect getEndOffset();
+    method public androidx.ui.core.PxPosition getStartCoordinates();
     method public androidx.ui.core.LayoutCoordinates? getStartLayoutCoordinates();
-    method public androidx.ui.engine.geometry.Rect getStartOffset();
   }
 
   public final class SelectionContainerKt {
@@ -278,6 +313,10 @@
     method public static void SelectionContainer(androidx.ui.core.selection.Selection? selection, kotlin.jvm.functions.Function1<? super androidx.ui.core.selection.Selection,kotlin.Unit> onSelectionChange, androidx.ui.core.selection.SelectionMode mode = SelectionMode.Vertical, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
+  public final class SelectionHandlesKt {
+    ctor public SelectionHandlesKt();
+  }
+
   public final class SelectionKt {
     ctor public SelectionKt();
   }
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 7e4b05c..c7820fa 100644
--- a/ui/ui-framework/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-framework/api/restricted_1.0.0-alpha01.txt
@@ -34,7 +34,7 @@
 
   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 = {});
+    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 {
@@ -92,6 +92,11 @@
     property public abstract Object? parentData;
   }
 
+  public interface OffsetMap {
+    method public int originalToTransformed(int offset);
+    method public int transformedToOriginal(int offset);
+  }
+
   public final class OpacityKt {
     ctor public OpacityKt();
     method public static void Opacity(@FloatRange(from=0.0, to=1.0) float opacity, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -102,6 +107,13 @@
     method public static void ParentData(Object data, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
+  public final class PasswordVisualTransformation implements androidx.ui.core.VisualTransformation {
+    ctor public PasswordVisualTransformation(char mask);
+    ctor public PasswordVisualTransformation();
+    method public androidx.ui.core.TransformedText filter(androidx.ui.text.AnnotatedString text);
+    method public char getMask();
+  }
+
   public abstract class Placeable {
     ctor public Placeable();
     method public abstract androidx.ui.core.IntPx getHeight();
@@ -174,12 +186,30 @@
     method public androidx.ui.core.TextSpanComposition getComposer();
   }
 
+  public final class TransformedText {
+    ctor public TransformedText(androidx.ui.text.AnnotatedString transformedText, androidx.ui.core.OffsetMap offsetMap);
+    method public androidx.ui.text.AnnotatedString component1();
+    method public androidx.ui.core.OffsetMap component2();
+    method public androidx.ui.core.TransformedText copy(androidx.ui.text.AnnotatedString transformedText, androidx.ui.core.OffsetMap offsetMap);
+    method public androidx.ui.core.OffsetMap getOffsetMap();
+    method public androidx.ui.text.AnnotatedString getTransformedText();
+  }
+
+  public interface VisualTransformation {
+    method public androidx.ui.core.TransformedText filter(androidx.ui.text.AnnotatedString text);
+  }
+
+  public final class VisualTransformationKt {
+    ctor public VisualTransformationKt();
+  }
+
   public final class WrapperKt {
     ctor public WrapperKt();
     method public static void ComposeView(kotlin.jvm.functions.Function0<kotlin.Unit> children);
     method public static void WithDensity(kotlin.jvm.functions.Function1<? super androidx.ui.core.DensityReceiver,kotlin.Unit> block);
     method @CheckResult(suggest="+") public static androidx.compose.Effect<androidx.ui.core.Density> ambientDensity();
     method public static androidx.compose.Ambient<android.content.Context> getContextAmbient();
+    method public static androidx.compose.Ambient<kotlin.coroutines.CoroutineContext> getCoroutineContextAmbient();
     method public static androidx.compose.Ambient<androidx.ui.core.Density> getDensityAmbient();
     method public static androidx.compose.CompositionContext? setContent(android.app.Activity, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method public static androidx.compose.CompositionContext? setContent(android.view.ViewGroup, kotlin.jvm.functions.Function0<kotlin.Unit> content);
@@ -222,6 +252,11 @@
     method public default void onStop(androidx.ui.core.PxPosition velocity);
   }
 
+  public final class LongPressGestureDetectorKt {
+    ctor public LongPressGestureDetectorKt();
+    method public static void LongPressGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.PxPosition,kotlin.Unit> onLongPress, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+  }
+
   public final class PressGestureDetectorKt {
     ctor public PressGestureDetectorKt();
     method public static void PressGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.PxPosition,kotlin.Unit>? onPress = null, kotlin.jvm.functions.Function0<kotlin.Unit>? onRelease = null, kotlin.jvm.functions.Function0<kotlin.Unit>? onCancel = null, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -261,16 +296,16 @@
 package androidx.ui.core.selection {
 
   public final class Selection {
-    ctor public Selection(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
-    method public androidx.ui.engine.geometry.Rect component1();
-    method public androidx.ui.engine.geometry.Rect component2();
+    ctor public Selection(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+    method public androidx.ui.core.PxPosition component1();
+    method public androidx.ui.core.PxPosition component2();
     method public androidx.ui.core.LayoutCoordinates? component3();
     method public androidx.ui.core.LayoutCoordinates? component4();
-    method public androidx.ui.core.selection.Selection copy(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+    method public androidx.ui.core.selection.Selection copy(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+    method public androidx.ui.core.PxPosition getEndCoordinates();
     method public androidx.ui.core.LayoutCoordinates? getEndLayoutCoordinates();
-    method public androidx.ui.engine.geometry.Rect getEndOffset();
+    method public androidx.ui.core.PxPosition getStartCoordinates();
     method public androidx.ui.core.LayoutCoordinates? getStartLayoutCoordinates();
-    method public androidx.ui.engine.geometry.Rect getStartOffset();
   }
 
   public final class SelectionContainerKt {
@@ -278,6 +313,10 @@
     method public static void SelectionContainer(androidx.ui.core.selection.Selection? selection, kotlin.jvm.functions.Function1<? super androidx.ui.core.selection.Selection,kotlin.Unit> onSelectionChange, androidx.ui.core.selection.SelectionMode mode = SelectionMode.Vertical, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
+  public final class SelectionHandlesKt {
+    ctor public SelectionHandlesKt();
+  }
+
   public final class SelectionKt {
     ctor public SelectionKt();
   }
diff --git a/ui/ui-framework/api/restricted_current.txt b/ui/ui-framework/api/restricted_current.txt
index 7e4b05c..c7820fa 100644
--- a/ui/ui-framework/api/restricted_current.txt
+++ b/ui/ui-framework/api/restricted_current.txt
@@ -34,7 +34,7 @@
 
   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 = {});
+    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 {
@@ -92,6 +92,11 @@
     property public abstract Object? parentData;
   }
 
+  public interface OffsetMap {
+    method public int originalToTransformed(int offset);
+    method public int transformedToOriginal(int offset);
+  }
+
   public final class OpacityKt {
     ctor public OpacityKt();
     method public static void Opacity(@FloatRange(from=0.0, to=1.0) float opacity, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -102,6 +107,13 @@
     method public static void ParentData(Object data, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
+  public final class PasswordVisualTransformation implements androidx.ui.core.VisualTransformation {
+    ctor public PasswordVisualTransformation(char mask);
+    ctor public PasswordVisualTransformation();
+    method public androidx.ui.core.TransformedText filter(androidx.ui.text.AnnotatedString text);
+    method public char getMask();
+  }
+
   public abstract class Placeable {
     ctor public Placeable();
     method public abstract androidx.ui.core.IntPx getHeight();
@@ -174,12 +186,30 @@
     method public androidx.ui.core.TextSpanComposition getComposer();
   }
 
+  public final class TransformedText {
+    ctor public TransformedText(androidx.ui.text.AnnotatedString transformedText, androidx.ui.core.OffsetMap offsetMap);
+    method public androidx.ui.text.AnnotatedString component1();
+    method public androidx.ui.core.OffsetMap component2();
+    method public androidx.ui.core.TransformedText copy(androidx.ui.text.AnnotatedString transformedText, androidx.ui.core.OffsetMap offsetMap);
+    method public androidx.ui.core.OffsetMap getOffsetMap();
+    method public androidx.ui.text.AnnotatedString getTransformedText();
+  }
+
+  public interface VisualTransformation {
+    method public androidx.ui.core.TransformedText filter(androidx.ui.text.AnnotatedString text);
+  }
+
+  public final class VisualTransformationKt {
+    ctor public VisualTransformationKt();
+  }
+
   public final class WrapperKt {
     ctor public WrapperKt();
     method public static void ComposeView(kotlin.jvm.functions.Function0<kotlin.Unit> children);
     method public static void WithDensity(kotlin.jvm.functions.Function1<? super androidx.ui.core.DensityReceiver,kotlin.Unit> block);
     method @CheckResult(suggest="+") public static androidx.compose.Effect<androidx.ui.core.Density> ambientDensity();
     method public static androidx.compose.Ambient<android.content.Context> getContextAmbient();
+    method public static androidx.compose.Ambient<kotlin.coroutines.CoroutineContext> getCoroutineContextAmbient();
     method public static androidx.compose.Ambient<androidx.ui.core.Density> getDensityAmbient();
     method public static androidx.compose.CompositionContext? setContent(android.app.Activity, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method public static androidx.compose.CompositionContext? setContent(android.view.ViewGroup, kotlin.jvm.functions.Function0<kotlin.Unit> content);
@@ -222,6 +252,11 @@
     method public default void onStop(androidx.ui.core.PxPosition velocity);
   }
 
+  public final class LongPressGestureDetectorKt {
+    ctor public LongPressGestureDetectorKt();
+    method public static void LongPressGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.PxPosition,kotlin.Unit> onLongPress, kotlin.jvm.functions.Function0<kotlin.Unit> children);
+  }
+
   public final class PressGestureDetectorKt {
     ctor public PressGestureDetectorKt();
     method public static void PressGestureDetector(kotlin.jvm.functions.Function1<? super androidx.ui.core.PxPosition,kotlin.Unit>? onPress = null, kotlin.jvm.functions.Function0<kotlin.Unit>? onRelease = null, kotlin.jvm.functions.Function0<kotlin.Unit>? onCancel = null, kotlin.jvm.functions.Function0<kotlin.Unit> children);
@@ -261,16 +296,16 @@
 package androidx.ui.core.selection {
 
   public final class Selection {
-    ctor public Selection(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
-    method public androidx.ui.engine.geometry.Rect component1();
-    method public androidx.ui.engine.geometry.Rect component2();
+    ctor public Selection(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+    method public androidx.ui.core.PxPosition component1();
+    method public androidx.ui.core.PxPosition component2();
     method public androidx.ui.core.LayoutCoordinates? component3();
     method public androidx.ui.core.LayoutCoordinates? component4();
-    method public androidx.ui.core.selection.Selection copy(androidx.ui.engine.geometry.Rect startOffset, androidx.ui.engine.geometry.Rect endOffset, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+    method public androidx.ui.core.selection.Selection copy(androidx.ui.core.PxPosition startCoordinates, androidx.ui.core.PxPosition endCoordinates, androidx.ui.core.LayoutCoordinates? startLayoutCoordinates, androidx.ui.core.LayoutCoordinates? endLayoutCoordinates);
+    method public androidx.ui.core.PxPosition getEndCoordinates();
     method public androidx.ui.core.LayoutCoordinates? getEndLayoutCoordinates();
-    method public androidx.ui.engine.geometry.Rect getEndOffset();
+    method public androidx.ui.core.PxPosition getStartCoordinates();
     method public androidx.ui.core.LayoutCoordinates? getStartLayoutCoordinates();
-    method public androidx.ui.engine.geometry.Rect getStartOffset();
   }
 
   public final class SelectionContainerKt {
@@ -278,6 +313,10 @@
     method public static void SelectionContainer(androidx.ui.core.selection.Selection? selection, kotlin.jvm.functions.Function1<? super androidx.ui.core.selection.Selection,kotlin.Unit> onSelectionChange, androidx.ui.core.selection.SelectionMode mode = SelectionMode.Vertical, kotlin.jvm.functions.Function0<kotlin.Unit> children);
   }
 
+  public final class SelectionHandlesKt {
+    ctor public SelectionHandlesKt();
+  }
+
   public final class SelectionKt {
     ctor public SelectionKt();
   }
diff --git a/ui/ui-framework/build.gradle b/ui/ui-framework/build.gradle
index 6d07e4c..2bf6a35 100644
--- a/ui/ui-framework/build.gradle
+++ b/ui/ui-framework/build.gradle
@@ -30,7 +30,7 @@
 
 dependencies {
     kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
-
+    implementation(KOTLIN_COMPOSE_COROUTINES)
     implementation(KOTLIN_COMPOSE_STDLIB)
 
     // TODO: Non-Kotlin dependency, move to Android-specific code
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/AndroidManifest.xml b/ui/ui-framework/integration-tests/framework-demos/src/main/AndroidManifest.xml
index ed79eaf..933fee0 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/AndroidManifest.xml
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/AndroidManifest.xml
@@ -45,6 +45,14 @@
                 <category android:name="androidx.ui.demos.SAMPLE_CODE" />
             </intent-filter>
         </activity>
+        <activity android:name=".gestures.LongPressGestureDetectorDemo"
+            android:configChanges="orientation|screenSize"
+            android:label="Gestures/Single GestureDetectors/LongPressGestureDetectorDemo">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="androidx.ui.demos.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
         <activity android:name=".gestures.NestedScrollingDemo"
             android:configChanges="orientation|screenSize"
             android:label="Gestures/Complex Demos/Nested Scrolling">
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/Colors.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/Colors.kt
new file mode 100644
index 0000000..eaea537
--- /dev/null
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/Colors.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.framework.demos.gestures
+
+import androidx.ui.graphics.Color
+import kotlin.random.Random
+
+val Red = Color(0xFFf44336.toInt())
+val Pink = Color(0xFFe91e63.toInt())
+val Purple = Color(0xFF9c27b0.toInt())
+val DeepPurple = Color(0xFF673ab7.toInt())
+val Indigo = Color(0xFF3f51b5.toInt())
+val Blue = Color(0xFF2196f3.toInt())
+val LightBlue = Color(0xFF03a9f4.toInt())
+val Cyan = Color(0xFF00bcd4.toInt())
+val Teal = Color(0xFF009688.toInt())
+val Green = Color(0xFF4caf50.toInt())
+val LightGreen = Color(0xFF8bc34a.toInt())
+val Lime = Color(0xFFcddc39.toInt())
+val Yellow = Color(0xFFffeb3b.toInt())
+val Amber = Color(0xFFffc107.toInt())
+val Orange = Color(0xFFff9800.toInt())
+val DeepOrange = Color(0xFFff5722.toInt())
+val Brown = Color(0xFF795548.toInt())
+val Grey = Color(0xFF9e9e9e.toInt())
+val BlueGrey = Color(0xFF607d8b.toInt())
+
+val Colors = listOf(
+    Red,
+    Pink,
+    Purple,
+    DeepPurple,
+    Indigo,
+    Blue,
+    LightBlue,
+    Cyan,
+    Teal,
+    Green,
+    LightGreen,
+    Lime,
+    Yellow,
+    Amber,
+    Orange,
+    DeepOrange,
+    Brown,
+    Grey,
+    BlueGrey
+)
+
+fun Color.anotherRandomColor() = Colors.random(this)
+
+fun Color.next() = Colors.inOrder(this, true)
+fun Color.prev() = Colors.inOrder(this, false)
+
+fun List<Color>.random(exclude: Color?): Color {
+    val excludeIndex = indexOf(exclude)
+
+    val max = size - if (excludeIndex >= 0) 1 else 0
+
+    val random = Random.nextInt(max).run {
+        if (excludeIndex >= 0 && this >= excludeIndex) {
+            this + 1
+        } else {
+            this
+        }
+    }
+
+    return this[random]
+}
+
+fun List<Color>.inOrder(current: Color?, forward: Boolean): Color {
+    val currentIndex = indexOf(current)
+
+    val next =
+        if (forward) {
+            if (currentIndex == -1) {
+                0
+            } else {
+                (currentIndex + 1) % size
+            }
+        } else {
+            if (currentIndex == -1) {
+                size - 1
+            } else {
+                (currentIndex - 1 + size) % size
+            }
+        }
+
+    return this[next]
+}
\ No newline at end of file
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/DragGestureDetectorDemo.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/DragGestureDetectorDemo.kt
index af0161a..f7dabe6 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/DragGestureDetectorDemo.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/DragGestureDetectorDemo.kt
@@ -23,10 +23,11 @@
 import androidx.ui.core.PxPosition
 import androidx.ui.core.gesture.DragObserver
 import androidx.ui.core.px
+import androidx.ui.core.setContent
 import androidx.ui.graphics.Color
 import androidx.compose.composer
+import androidx.ui.core.dp
 import androidx.ui.core.gesture.DragGestureDetector
-import androidx.ui.core.setContent
 
 /**
  * Simple demo that shows off DragGestureDetector.
@@ -51,8 +52,8 @@
                     DrawBox(
                         xOffset.value,
                         yOffset.value,
-                        200.px,
-                        200.px,
+                        96.dp,
+                        96.dp,
                         Color(0xFF9e9e9e.toInt())
                     )
                 }
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/LongPressGestureDetectorDemo.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/LongPressGestureDetectorDemo.kt
new file mode 100644
index 0000000..0cd4f12
--- /dev/null
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/gestures/LongPressGestureDetectorDemo.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.framework.demos.gestures
+
+import android.app.Activity
+import android.os.Bundle
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.ui.core.PxPosition
+import androidx.ui.core.px
+import androidx.ui.core.setContent
+import androidx.compose.composer
+import androidx.ui.core.dp
+import androidx.ui.core.gesture.LongPressGestureDetector
+
+/**
+ * Simple demo that shows off DragGestureDetector.
+ */
+class LongPressGestureDetectorDemo : Activity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent {
+            val color = +state { Colors.random() }
+
+            val onLongPress = { _: PxPosition ->
+                color.value = color.value.anotherRandomColor()
+            }
+
+            LongPressGestureDetector(onLongPress = onLongPress) {
+                MatchParent {
+                    DrawBox(
+                        0.px,
+                        0.px,
+                        96.dp,
+                        96.dp,
+                        color.value
+                    )
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
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 57886b2..dc3feee 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
@@ -24,24 +24,26 @@
 import androidx.compose.unaryPlus
 import androidx.ui.core.Direction
 import androidx.ui.core.Dp
-import androidx.ui.core.Draw
 import androidx.ui.core.IntPx
 import androidx.ui.core.Layout
 import androidx.ui.core.PxPosition
 import androidx.ui.core.coerceIn
-import androidx.ui.core.dp
 import androidx.ui.core.gesture.DragGestureDetector
 import androidx.ui.core.gesture.DragObserver
 import androidx.ui.core.gesture.PressIndicatorGestureDetector
 import androidx.ui.core.ipx
 import androidx.ui.core.px
-import androidx.ui.core.round
-import androidx.ui.core.toRect
+import androidx.ui.core.setContent
 import androidx.ui.engine.geometry.Rect
 import androidx.ui.graphics.Color
 import androidx.ui.painting.Paint
+import androidx.ui.core.gesture.LongPressGestureDetector
 import androidx.compose.composer
-import androidx.ui.core.setContent
+import androidx.ui.core.Draw
+import androidx.ui.core.dp
+import androidx.ui.core.gesture.PressReleasedGestureDetector
+import androidx.ui.core.round
+import androidx.ui.core.toRect
 
 /**
  * Demo app created to study some complex interactions of multiple DragGestureDetectors.
@@ -50,11 +52,11 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContent {
-            // Outer composable that scrolls
+            // Outer composable that scrollsAll mea
             Draggable {
                 RepeatingList(repititions = 3) {
                     SimpleContainer(
-                        width = -1.dp,
+                        width = (-1).dp,
                         height = 398.dp,
                         padding = 72.dp
                     ) {
@@ -138,6 +140,10 @@
     height: Dp
 ) {
 
+    val pressedColor = Color(0x1f000000)
+    val itemColor = Color(0xFFFFFFFF.toInt())
+
+    val color = +state { itemColor }
     val pressed = +state { false }
 
     val onStart: (PxPosition) -> Unit = {
@@ -148,29 +154,43 @@
         pressed.value = false
     }
 
-    val resolvedColor =
-        if (pressed.value) {
-            Color(0x1f000000)
-        } else {
-            Color(0xFFFFFFFF.toInt())
-        }
+    val onRelease = {
+        color.value = color.value.next()
+        pressed.value = false
+    }
+
+    val onLongPress = { _: PxPosition ->
+        color.value = color.value.prev()
+        pressed.value = false
+    }
 
     val children = @Composable {
         Draw { canvas, parentSize ->
-            val backgroundPaint = Paint().apply { this.color = resolvedColor }
+            val backgroundPaint = Paint().apply { this.color = color.value }
             canvas.drawRect(
                 Rect(0f, 0f, parentSize.width.value, parentSize.height.value),
                 backgroundPaint
             )
+            if (pressed.value) {
+                backgroundPaint.color = pressedColor
+                canvas.drawRect(
+                    Rect(0f, 0f, parentSize.width.value, parentSize.height.value),
+                    backgroundPaint
+                )
+            }
         }
     }
 
     PressIndicatorGestureDetector(onStart, onStop, onStop) {
-        Layout(children) { _, constraints ->
-            layout(
-                constraints.maxWidth,
-                height.toIntPx().coerceIn(constraints.minHeight, constraints.maxHeight)
-            ) {}
+        PressReleasedGestureDetector(onRelease, false) {
+            LongPressGestureDetector(onLongPress) {
+                Layout(children) { _, constraints ->
+                    layout(
+                        constraints.maxWidth,
+                        height.toIntPx().coerceIn(constraints.minHeight, constraints.maxHeight)
+                    ) {}
+                }
+            }
         }
     }
 }
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 8e67db6..e49a48e 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
@@ -175,8 +175,8 @@
 internal fun DrawBox(
     xOffset: Px,
     yOffset: Px,
-    width: Px,
-    height: Px,
+    width: Dp,
+    height: Dp,
     color: Color
 ) {
     val paint = +memo { Paint() }
@@ -184,8 +184,10 @@
         paint.color = color
         val centerX = parentSize.width.value / 2 + xOffset.value
         val centerY = parentSize.height.value / 2 + yOffset.value
-        val widthValue = if (width.value < 0) parentSize.width.value else width.value
-        val heightValue = if (height.value < 0) parentSize.height.value else height.value
+        val widthPx = width.toPx()
+        val heightPx = height.toPx()
+        val widthValue = if (widthPx.value < 0) parentSize.width.value else widthPx.value
+        val heightValue = if (heightPx.value < 0) parentSize.height.value else heightPx.value
         canvas.drawRect(
             androidx.ui.engine.geometry.Rect(
                 centerX - widthValue / 2,
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/InputField.kt b/ui/ui-framework/src/main/java/androidx/ui/core/InputField.kt
index a2bb749..5da2696 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/InputField.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/InputField.kt
@@ -31,7 +31,6 @@
 import androidx.ui.input.EditorState
 import androidx.ui.input.ImeAction
 import androidx.ui.input.KeyboardType
-import androidx.ui.text.AnnotatedString
 import androidx.ui.text.TextPainter
 import androidx.ui.text.TextStyle
 
@@ -100,7 +99,12 @@
     onValueChange: (EditorState) -> Unit = {},
 
     /** Called when the InputMethod requested an IME action */
-    onImeActionPerformed: (ImeAction) -> Unit = {} // TODO(nona): Define argument type
+    onImeActionPerformed: (ImeAction) -> Unit = {},
+
+    /**
+     * Optional visual filter for changing visual output of input field.
+     */
+    visualTransformation: VisualTransformation? = null
 ) {
     // Ambients
     val style = +ambient(CurrentTextStyleAmbient)
@@ -111,10 +115,13 @@
     // Memos
     val processor = +memo { EditProcessor() }
     val mergedStyle = style.merge(editorStyle.textStyle)
-    val textPainter = +memo(value, mergedStyle, density, resourceLoader) {
+    val (visualText, offsetMap) = +memo(value, visualTransformation) {
+        InputFieldDelegate.applyVisualFilter(value, visualTransformation)
+    }
+    val textPainter = +memo(visualText, mergedStyle, density, resourceLoader) {
         // TODO(nona): Add parameter for text direction, softwrap, etc.
         TextPainter(
-            text = AnnotatedString(text = value.text),
+            text = visualText,
             style = mergedStyle,
             density = density,
             resourceLoader = resourceLoader
@@ -123,10 +130,11 @@
 
     // States
     val hasFocus = +state { false }
+    val coords = +state<LayoutCoordinates?> { null }
 
     processor.onNewState(value, textInputService)
     TextInputEventObserver(
-        onPress = { InputFieldDelegate.onPress(textInputService) },
+        onPress = { },
         onFocus = {
             hasFocus.value = true
             InputFieldDelegate.onFocus(
@@ -137,6 +145,18 @@
                 imeAction,
                 onValueChange,
                 onImeActionPerformed)
+            coords.value?.let { coords ->
+                textInputService?.let { textInputService ->
+                    InputFieldDelegate.notifyFocusedRect(
+                        value,
+                        textPainter,
+                        coords,
+                        textInputService,
+                        hasFocus.value,
+                        offsetMap
+                    )
+                }
+            }
         },
         onBlur = {
             hasFocus.value = false
@@ -146,7 +166,16 @@
                 onValueChange)
         },
         onDragAt = { InputFieldDelegate.onDragAt(it) },
-        onRelease = { InputFieldDelegate.onRelease(it, textPainter, processor, onValueChange) }
+        onRelease = {
+            InputFieldDelegate.onRelease(
+                it,
+                textPainter,
+                processor,
+                offsetMap,
+                onValueChange,
+                textInputService,
+                hasFocus.value)
+        }
     ) {
         Layout(
             children = @Composable {
@@ -154,18 +183,21 @@
                     if (textInputService != null) {
                         // TODO(nona): notify focused rect in onPreDraw equivalent callback for
                         //             supporting multiline text.
+                        coords.value = it
                         InputFieldDelegate.notifyFocusedRect(
                             value,
                             textPainter,
                             it,
                             textInputService,
-                            hasFocus.value
+                            hasFocus.value,
+                            offsetMap
                         )
                     }
                 }
                 Draw { canvas, _ -> InputFieldDelegate.draw(
                     canvas,
                     value,
+                    offsetMap,
                     textPainter,
                     hasFocus.value,
                     editorStyle) }
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/InputFieldDelegate.kt b/ui/ui-framework/src/main/java/androidx/ui/core/InputFieldDelegate.kt
index 3732906..1c70f23 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/InputFieldDelegate.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/InputFieldDelegate.kt
@@ -17,7 +17,6 @@
 package androidx.ui.core
 
 import android.util.Log
-import androidx.ui.engine.geometry.Offset
 import androidx.ui.engine.geometry.Rect
 import androidx.ui.input.EditOperation
 import androidx.ui.input.EditProcessor
@@ -73,6 +72,7 @@
          *
          * @param canvas The target canvas.
          * @param value The editor state
+         * @param offsetMap The offset map
          * @param textPainter The text painter
          * @param hasFocus true if this widget is focused, otherwise false
          * @param editorStyle The editor style.
@@ -81,33 +81,33 @@
         fun draw(
             canvas: Canvas,
             value: EditorState,
+            offsetMap: OffsetMap,
             textPainter: TextPainter,
             hasFocus: Boolean,
             editorStyle: EditorStyle
         ) {
             value.composition?.let {
                 textPainter.paintBackground(
-                    it.start,
-                    it.end,
+                    offsetMap.originalToTransformed(it.start),
+                    offsetMap.originalToTransformed(it.end),
                     editorStyle.compositionColor,
-                    canvas,
-                    Offset.zero
+                    canvas
                 )
             }
             if (value.selection.collapsed) {
                 if (hasFocus) {
-                    textPainter.paintCursor(value.selection.start, canvas)
+                    textPainter.paintCursor(
+                        offsetMap.originalToTransformed(value.selection.start), canvas)
                 }
             } else {
                 textPainter.paintBackground(
-                    value.selection.start,
-                    value.selection.end,
+                    offsetMap.originalToTransformed(value.selection.start),
+                    offsetMap.originalToTransformed(value.selection.end),
                     editorStyle.selectionColor,
-                    canvas,
-                    Offset.zero
+                    canvas
                 )
             }
-            textPainter.paint(canvas, Offset.zero)
+            textPainter.paint(canvas)
         }
 
         /**
@@ -121,18 +121,19 @@
             textPainter: TextPainter,
             layoutCoordinates: LayoutCoordinates,
             textInputService: TextInputService,
-            hasFocus: Boolean
+            hasFocus: Boolean,
+            offsetMap: OffsetMap
         ) {
             if (!hasFocus) {
                 return
             }
 
             val bbox = if (value.selection.end < value.text.length) {
-                textPainter.getBoundingBox(value.selection.end)
+                textPainter.getBoundingBox(offsetMap.originalToTransformed(value.selection.end))
             } else if (value.selection.end != 0) {
-                textPainter.getBoundingBox(value.selection.end - 1)
+                textPainter.getBoundingBox(offsetMap.originalToTransformed(value.selection.end) - 1)
             } else {
-                Rect(0f, 0f, 0f, 0f)
+                Rect(0f, 0f, 1.0f, textPainter.preferredLineHeight)
             }
             val globalLT = layoutCoordinates.localToRoot(PxPosition(bbox.left.px, bbox.top.px))
 
@@ -163,16 +164,6 @@
         }
 
         /**
-         * Called when onPress event is fired.
-         *
-         * @param textInputService The text input service
-         */
-        @JvmStatic
-        fun onPress(textInputService: TextInputService?) {
-            textInputService?.showSoftwareKeyboard()
-        }
-
-        /**
          * Called when onDrag event is fired.
          *
          * @param position The event position in widget coordinate.
@@ -189,17 +180,30 @@
          * @param position The event position in widget coordinate.
          * @param textPainter The text painter
          * @param editProcessor The edit processor
+         * @param offsetMap The offset map
          * @param onValueChange The callback called when the new editor state arrives.
+         * @param textInputService The text input service
+         * @param hasFocus True if the widget has input focus, otherwise false.
          */
         @JvmStatic
         fun onRelease(
             position: PxPosition,
             textPainter: TextPainter,
             editProcessor: EditProcessor,
-            onValueChange: (EditorState) -> Unit
+            offsetMap: OffsetMap,
+            onValueChange: (EditorState) -> Unit,
+            textInputService: TextInputService?,
+            hasFocus: Boolean
         ) {
-            val offset = textPainter.getPositionForOffset(position.toOffset())
-            onEditCommand(listOf(SetSelectionEditOp(offset, offset)), editProcessor, onValueChange)
+            textInputService?.showSoftwareKeyboard()
+            if (hasFocus) {
+                val offset = offsetMap.transformedToOriginal(
+                    textPainter.getOffsetForPosition(position))
+                onEditCommand(
+                    listOf(SetSelectionEditOp(offset, offset)),
+                    editProcessor,
+                    onValueChange)
+            }
         }
 
         /**
@@ -210,7 +214,7 @@
          * @param editProcessor The edit processor
          * @param keyboardType The keyboard type
          * @param onValueChange The callback called when the new editor state arrives.
-         * @param onEditorActionPerformed The callback called when the editor action arrives.
+         * @param onImeActionPerformed The callback called when the editor action arrives.
          */
         @JvmStatic
         fun onFocus(
@@ -246,5 +250,21 @@
             onEditCommand(listOf(FinishComposingTextEditOp()), editProcessor, onValueChange)
             textInputService?.stopInput()
         }
+
+        /**
+         * Helper function of applying visual transformation method to the EditorState.
+         *
+         * @param value An editor state
+         * @param visualTransformation A visual transformation
+         */
+        @JvmStatic
+        fun applyVisualFilter(
+            value: EditorState,
+            visualTransformation: VisualTransformation?
+        ): TransformedText {
+            val annotatedString = AnnotatedString(value.text)
+            return visualTransformation?.filter(annotatedString)
+                    ?: TransformedText(annotatedString, identityOffsetMap)
+        }
     }
 }
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 524f483..7e1680cb 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
@@ -31,7 +31,6 @@
 import androidx.ui.core.selection.Selection
 import androidx.ui.core.selection.SelectionMode
 import androidx.ui.core.selection.SelectionRegistrarAmbient
-import androidx.ui.engine.geometry.Offset
 import androidx.ui.graphics.Color
 import androidx.ui.text.AnnotatedString
 import androidx.ui.text.ParagraphStyle
@@ -228,9 +227,9 @@
             Draw { canvas, _ ->
                 internalSelection.value?.let {
                     textPainter.paintBackground(
-                        it.start, it.end, selectionColor, canvas, Offset.zero)
+                        it.start, it.end, selectionColor, canvas)
                 }
-                textPainter.paint(canvas, Offset.zero)
+                textPainter.paint(canvas)
             }
         }
         ComplexLayout(children) {
@@ -286,13 +285,12 @@
                             onSelectionChange = { internalSelection.value = it },
                             textPainter = textPainter
                         )
-
                         if (!textSelectionProcessor.isSelected) return null
 
                         // TODO(qqd): Determine a set of coordinates around a character that we need.
                         return Selection(
-                            startOffset = textSelectionProcessor.startOffset,
-                            endOffset = textSelectionProcessor.endOffset,
+                            startCoordinates = textSelectionProcessor.startCoordinates,
+                            endCoordinates = textSelectionProcessor.endCoordinates,
                             startLayoutCoordinates =
                             if (textSelectionProcessor.containsWholeSelectionStart) {
                                 layoutCoordinates.value!!
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/VisualTransformation.kt b/ui/ui-framework/src/main/java/androidx/ui/core/VisualTransformation.kt
new file mode 100644
index 0000000..23ff8a6
--- /dev/null
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/VisualTransformation.kt
@@ -0,0 +1,142 @@
+/*
+ * 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
+
+import androidx.ui.text.AnnotatedString
+
+/**
+ * The map interface used for bidirectional offset mapping from original to transformed text.
+ */
+interface OffsetMap {
+    /**
+     * Convert offset in original text into the offset in transformed text.
+     *
+     * This function must be a monotonically non-decreasing function. In other words, if a cursor
+     * advances in the original text, the cursor in the transformed text must advance or stay there.
+     *
+     * @param offset offset in original text.
+     * @return offset in transformed text
+     * @see VisualTransformation
+     */
+    fun originalToTransformed(offset: Int): Int
+
+    /**
+     * Convert offset in transformed text into the offset in original text.
+     *
+     * This function must be a monotonically non-decreasing function. In other words, if a cursor
+     * advances in the transformed text, the cusrsor in the original text must advance or stay
+     * there.
+     *
+     * @param offset offset in transformed text
+     * @return offset in original text
+     * @see VisualTransformation
+     */
+    fun transformedToOriginal(offset: Int): Int
+}
+
+/**
+ * The transformed text with offset offset mapping
+ */
+data class TransformedText(
+    /**
+     * The transformed text
+     */
+    val transformedText: AnnotatedString,
+
+    /**
+     * The map used for bidirectional offset mapping from original to transformed text.
+     */
+    val offsetMap: OffsetMap
+)
+
+/**
+ * Interface used for changing visual output of the input field.
+ *
+ * This interface can be used for changing visual output of the text in the input field.
+ * For example, you can mask characters in password filed with asterisk with
+ * PasswordVisualTransformation.
+ */
+interface VisualTransformation {
+    /**
+     * Change the visual output of given text.
+     *
+     * Note that the returned text length can be different length from the given text. The widget
+     * will call the offset translator for converting offsets for various reasons, cursor drawing
+     * position, text selection by gesture, etc.
+     *
+     * Example: Credit Card Visual Output (inserting hyphens each 4 digits)
+     *  original text   : 1234567890123456
+     *  transformed text: 1234-5678-9012-3456
+     *
+     *  Then, the offset translator should ignore the hyphen characters, so conversion from
+     *  original offset to transformed text works like
+     *  - The 4th char of the original text is 5th char in the transformed text.
+     *  - The 13th char of the original text is 15th char in the transformed text.
+     *  Similarly, the reverse conversion works like
+     *  - The 5th char of the transformed text is 4th char in the original text.
+     *  - The 12th char of the transformed text is 10th char in the original text.
+     *
+     *  The reference implementation would be like as follows:
+     *  <pre>
+     *  val creditCardOffsetTranslator = object : OffsetMap {
+     *      override fun originalToTransformed(originalOffset: Int): Int {
+     *          if (originalOffset <= 3) return originalOffset
+     *          if (originalOffset <= 7) return originalOffset + 1
+     *          if (originalOffset <= 11) return originalOffset + 2
+     *          if (originalOffset <= 16) return originalOffset + 3
+     *          return 19
+     *      }
+     *
+     *      override fun transformedToOriginal(transformedOffset: Int): Int {
+     *          if (transformedOffset <= 4) return transformedOffset
+     *          if (transformedOffset <= 9) return transformedOffset - 1
+     *          if (transformedOffset <= 14) return transformedOffset - 2
+     *          if (transformedOffset <= 19) return transformedOffset - 3
+     *          return 16
+     *      }
+     *  }
+     *  </pre>
+     *
+     * TODO(nona): Add paragraph direction argument for determining offset conversion.
+     *
+     * @param text The original text
+     * @return the pair of filtered text and offset translator.
+     */
+    fun filter(text: AnnotatedString): TransformedText
+}
+
+/**
+ * The Visual Filter can be used for password Input Field.
+ *
+ * Note that this visual filter only works for ASCII characters.
+ *
+ * @param mask The mask character used instead of original text.
+ */
+class PasswordVisualTransformation(val mask: Char = '\u2022') : VisualTransformation {
+    override fun filter(text: AnnotatedString): TransformedText {
+        return TransformedText(AnnotatedString(Character.toString(mask).repeat(text.text.length)),
+            identityOffsetMap)
+    }
+}
+
+/**
+ * The offset map used for identity mapping.
+ */
+internal val identityOffsetMap = object : OffsetMap {
+    override fun originalToTransformed(offset: Int): Int = offset
+    override fun transformedToOriginal(offset: Int): Int = offset
+}
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 272f1d7..45f178a 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
@@ -37,6 +37,8 @@
 import androidx.compose.unaryPlus
 import androidx.ui.core.text.AndroidFontResourceLoader
 import androidx.ui.text.font.Font
+import kotlinx.coroutines.Dispatchers
+import kotlin.coroutines.CoroutineContext
 
 /**
  * Composes a view containing ui composables into a view composition.
@@ -80,8 +82,11 @@
         val rootLayoutNode = rootRef.value?.root ?: error("Failed to create root platform view")
         val context = rootRef.value?.context ?: composer.composer.context
 
+        // If this value is inlined where it is used, an error that includes 'Precise Reference:
+        // kotlinx.coroutines.Dispatchers' not instance of 'Precise Reference: androidx.compose.Ambient'.
+        val coroutineContext = Dispatchers.Main
         cc = Compose.composeInto(container = rootLayoutNode, context = context, parent = reference) {
-            WrapWithAmbients(rootRef.value!!, context) {
+            WrapWithAmbients(rootRef.value!!, context, coroutineContext) {
                 children()
             }
         }
@@ -102,8 +107,11 @@
         .getChildAt(0) as? AndroidCraneView
         ?: AndroidCraneView(this).also { setContentView(it) }
 
+    // If this value is inlined where it is used, an error that includes 'Precise Reference:
+    // kotlinx.coroutines.Dispatchers' not instance of 'Precise Reference: androidx.compose.Ambient'.
+    val coroutineContext = Dispatchers.Main
     return Compose.composeInto(craneView.root, this) {
-        WrapWithAmbients(craneView, this) {
+        WrapWithAmbients(craneView, this, coroutineContext) {
             content()
         }
     }
@@ -121,8 +129,11 @@
         if (childCount > 0) { getChildAt(0) as? AndroidCraneView } else { removeAllViews(); null }
         ?: AndroidCraneView(context).also { addView(it) }
 
+    // If this value is inlined where it is used, an error that includes 'Precise Reference:
+    // kotlinx.coroutines.Dispatchers' not instance of 'Precise Reference: androidx.compose.Ambient'.
+    val coroutineContext = Dispatchers.Main
     return Compose.composeInto(craneView.root, context) {
-        WrapWithAmbients(craneView, context) {
+        WrapWithAmbients(craneView, context, coroutineContext) {
             content()
         }
     }
@@ -132,12 +143,14 @@
 private fun WrapWithAmbients(
     craneView: AndroidCraneView,
     context: Context,
+    coroutineContext: CoroutineContext,
     @Children content: @Composable() () -> Unit
 ) {
     // TODO(nona): Tie the focus manger lifecycle to Window, otherwise FocusManager won't work
     //             with nested AndroidCraneView case
     val focusManager = +memo { FocusManager() }
     ContextAmbient.Provider(value = context) {
+        CoroutineContextAmbient.Provider(value = coroutineContext) {
         DensityAmbient.Provider(value = Density(context)) {
             FocusManagerAmbient.Provider(value = focusManager) {
                 TextInputServiceAmbient.Provider(value = craneView.textInputService) {
@@ -147,6 +160,7 @@
                 }
             }
         }
+        }
     }
 }
 
@@ -154,6 +168,8 @@
 
 val DensityAmbient = Ambient.of<Density>()
 
+val CoroutineContextAmbient = Ambient.of<CoroutineContext>()
+
 internal val FocusManagerAmbient = Ambient.of<FocusManager>()
 
 internal val TextInputServiceAmbient = Ambient.of<TextInputService?>()
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
new file mode 100644
index 0000000..a3f894c
--- /dev/null
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/gesture/LongPressGestureDetector.kt
@@ -0,0 +1,133 @@
+/*
+ * 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.gesture
+
+import androidx.ui.core.PointerEventPass
+import androidx.ui.core.PointerInputChange
+import androidx.ui.core.anyPositionChangeConsumed
+import androidx.ui.core.changedToDown
+import androidx.compose.Children
+import androidx.compose.Composable
+import androidx.compose.ambient
+import androidx.compose.memo
+import androidx.compose.unaryPlus
+import androidx.ui.core.PxPosition
+import androidx.ui.core.changedToUp
+import androidx.ui.core.changedToUpIgnoreConsumed
+import androidx.ui.core.consumeDownChange
+import androidx.compose.composer
+import androidx.ui.core.CoroutineContextAmbient
+import androidx.ui.core.PointerInputWrapper
+import androidx.ui.temputils.delay
+import kotlinx.coroutines.Job
+import kotlin.coroutines.CoroutineContext
+
+// TODO(b/137569202): This bug tracks the note below regarding the need to eventually improve LongPressGestureDetector.
+/**
+ * Responds to a pointer being "down" for an extended amount of time.
+ *
+ * Note: this is likely a temporary, naive, and flawed approach. It is not necessarily guaranteed to interoperate well
+ * with forthcoming behavior related to disambiguation between multi-tap (double tap, triple tap) and tap.
+ */
+@Composable
+fun LongPressGestureDetector(
+    onLongPress: (PxPosition) -> Unit,
+    @Children children: @Composable() () -> Unit
+) {
+    val recognizer =
+        +memo { LongPressGestureRecognizer(onLongPress, +ambient(CoroutineContextAmbient)) }
+    PointerInputWrapper(pointerInputHandler = recognizer.pointerInputHandler) {
+        children()
+    }
+}
+
+internal class LongPressGestureRecognizer(
+    val onLongPress: (PxPosition) -> Unit,
+    coroutineContext: CoroutineContext
+) {
+
+    private enum class State {
+        Idle, Primed, Fired
+    }
+
+    private var state = State.Idle
+    private val pointerPositions = linkedMapOf<Int, PxPosition>()
+    var longPressTimeout = LongPressTimeout
+    var job: Job? = null
+
+    val pointerInputHandler =
+        { changes: List<PointerInputChange>, pass: PointerEventPass ->
+
+            var changesToReturn = changes
+
+            if (pass == PointerEventPass.InitialDown && state == State.Fired) {
+                // If we are in the Fired state, we dispatched the long press event and pointers are still down so we
+                // should consume any up events to prevent other gesture detectors from responding to up.
+                changesToReturn = changesToReturn.map {
+                    if (it.changedToUp()) {
+                        it.consumeDownChange()
+                    } else {
+                        it
+                    }
+                }
+            }
+
+            if (pass == PointerEventPass.PostUp) {
+                if (state == State.Idle && changes.all { it.changedToDown() }) {
+                    // If we have not yet started and all of the changes changed to down, we are
+                    // starting.
+                    job = delay(longPressTimeout, coroutineContext) {
+                        onLongPress.invoke(pointerPositions.asIterable().first().value)
+                        state = State.Fired
+                    }
+                    pointerPositions.clear()
+                    state = State.Primed
+                } else if (state != State.Idle && changes.all { it.changedToUpIgnoreConsumed() }) {
+                    // If we have started and all of the changes changed to up, we are stopping.
+                    reset()
+                }
+
+                if (state == State.Primed) {
+                    // If we are primed, for all down pointers, keep track of their current positions, and for all
+                    // other pointers, remove their tracked information.
+                    changes.forEach {
+                        if (it.current.down) {
+                            pointerPositions[it.id] = it.current.position!!
+                        } else {
+                            pointerPositions.remove(it.id)
+                        }
+                    }
+                }
+            }
+
+            if (pass == PointerEventPass.PostDown &&
+                state == State.Primed &&
+                changes.any { it.anyPositionChangeConsumed() }
+            ) {
+                // If we are currently primed and any pointers had consumed movement, we should no longer fire the long
+                // press event so reset.
+                reset()
+            }
+
+            changesToReturn
+        }
+
+    private fun reset() {
+        job?.cancel()
+        state = State.Idle
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/Selection.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/Selection.kt
index f4c753a..6e2dd35 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/Selection.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/Selection.kt
@@ -17,24 +17,30 @@
 package androidx.ui.core.selection
 
 import androidx.ui.core.LayoutCoordinates
-import androidx.ui.engine.geometry.Rect
+import androidx.ui.core.PxPosition
 
 /**
  * Data class of Selection.
  */
 data class Selection(
     /**
-     * A box around the character at the start offset as Rect. This box' height is the line height,
-     * and the width is the advance. Note: It is temporary to use Rect.
+     * The coordinates of the graphical position for selection start character offset.
+     *
+     * This graphical position is the point at the left bottom corner for LTR
+     * character, or right bottom corner for RTL character.
+     *
+     * This coordinates is in child widget coordinates system.
      */
-    // TODO(qqd): After solving the problem of getting the coordinates of a character, figure out
-    // what should the startOffset and endOffset should be.
-    val startOffset: Rect,
+    val startCoordinates: PxPosition,
     /**
-     * A box around the character at the end offset as Rect. This box' height is the line height,
-     * and the width is the advance. Note: It is temporary to use Rect.
+     * The coordinates of the graphical position for selection end character offset.
+     *
+     * This graphical position is the point at the left bottom corner for LTR
+     * character, or right bottom corner for RTL character.
+     *
+     * This coordinates is in child widget coordinates system.
      */
-    val endOffset: Rect,
+    val endCoordinates: PxPosition,
     /**
      * The layout coordinates of the child which contains the start of the selection. If the child
      * does not contain the start of the selection, this should be null.
@@ -51,13 +57,13 @@
         var currentSelection = this.copy()
         if (other.startLayoutCoordinates != null) {
             currentSelection = currentSelection.copy(
-                startOffset = other.startOffset,
+                startCoordinates = other.startCoordinates,
                 startLayoutCoordinates = other.startLayoutCoordinates
             )
         }
         if (other.endLayoutCoordinates != null) {
             currentSelection = currentSelection.copy(
-                endOffset = other.endOffset,
+                endCoordinates = other.endCoordinates,
                 endLayoutCoordinates = other.endLayoutCoordinates
             )
         }
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 c238d48..a3bd5e5 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
@@ -22,22 +22,13 @@
 import androidx.compose.memo
 import androidx.compose.unaryPlus
 import androidx.ui.core.Constraints
-import androidx.ui.core.Draw
 import androidx.ui.core.IntPx
 import androidx.ui.core.Layout
 import androidx.ui.core.OnPositioned
-import androidx.ui.core.PxPosition
 import androidx.ui.core.gesture.DragGestureDetector
 import androidx.ui.core.gesture.PressIndicatorGestureDetector
 import androidx.ui.core.ipx
-import androidx.ui.core.px
 import androidx.ui.core.round
-import androidx.ui.core.toRect
-import androidx.ui.graphics.Color
-import androidx.ui.painting.Paint
-
-private val HANDLE_WIDTH = 100.px
-private val HANDLE_HEIGHT = 100.px
 
 /**
  * Selection Widget.
@@ -89,7 +80,7 @@
                 dragObserver = manager.handleDragObserver(dragStartHandle = true)
             ) {
                 Layout(
-                    children = { SelectionHandle() },
+                    children = { LeftPointingSelectionHandle() },
                     layoutBlock = { _, constraints ->
                         layout(constraints.minWidth, constraints.minHeight) {}
                     })
@@ -101,7 +92,7 @@
                 dragObserver = manager.handleDragObserver(dragStartHandle = false)
             ) {
                 Layout(
-                    children = { SelectionHandle() },
+                    children = { RightPointingSelectionHandle() },
                     layoutBlock = { _, constraints ->
                         layout(constraints.minWidth, constraints.minHeight) {}
                     })
@@ -136,31 +127,16 @@
                 ) {
                     val startOffset = manager.containerLayoutCoordinates.childToLocal(
                         selection.startLayoutCoordinates,
-                        PxPosition(
-                            selection.startOffset.left.px,
-                            selection.startOffset.bottom.px
-                        )
+                        selection.startCoordinates
                     )
                     val endOffset = manager.containerLayoutCoordinates.childToLocal(
                         selection.endLayoutCoordinates,
-                        PxPosition(
-                            selection.endOffset.right.px,
-                            selection.endOffset.bottom.px
-                        )
+                        selection.endCoordinates
                     )
-                    start.place(startOffset.x - HANDLE_WIDTH, startOffset.y - HANDLE_HEIGHT)
-                    end.place(endOffset.x, endOffset.y - HANDLE_HEIGHT)
+                    start.place(startOffset.x - HANDLE_WIDTH, startOffset.y)
+                    end.place(endOffset.x, endOffset.y)
                 }
             }
         }))
     }
 }
-
-@Composable
-internal fun SelectionHandle() {
-    val paint = +memo { Paint() }
-    paint.color = Color(0xAAD94633.toInt())
-    Draw { canvas, parentSize ->
-        canvas.drawRect(parentSize.toRect(), paint)
-    }
-}
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionHandles.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionHandles.kt
new file mode 100644
index 0000000..6c8d9f2
--- /dev/null
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionHandles.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.selection
+
+import androidx.compose.Composable
+import androidx.compose.composer
+import androidx.compose.memo
+import androidx.compose.unaryPlus
+import androidx.ui.core.Draw
+import androidx.ui.core.px
+import androidx.ui.engine.geometry.Rect
+import androidx.ui.graphics.Color
+import androidx.ui.painting.Paint
+import androidx.ui.painting.Path
+
+internal val HANDLE_WIDTH = 80.px
+internal val HANDLE_HEIGHT = 80.px
+private val HANDLE_COLOR = Color(0xFF2B28F5.toInt())
+
+@Composable
+internal fun LeftPointingSelectionHandle() {
+    val paint = +memo { Paint() }
+    paint.color = HANDLE_COLOR
+    Draw { canvas, _ ->
+        var path = Path()
+        path.addRect(
+            Rect(
+                top = 0f,
+                bottom = 0.5f * HANDLE_HEIGHT.value,
+                left = 0.5f * HANDLE_WIDTH.value,
+                right = HANDLE_WIDTH.value
+            )
+        )
+        path.addOval(
+            Rect(
+                top = 0f,
+                bottom = HANDLE_HEIGHT.value,
+                left = 0f,
+                right = HANDLE_WIDTH.value
+            )
+        )
+
+        canvas.drawPath(path, paint)
+    }
+}
+
+@Composable
+internal fun RightPointingSelectionHandle() {
+    val paint = +memo { Paint() }
+    paint.color = HANDLE_COLOR
+    Draw { canvas, _ ->
+        var path = Path()
+        path.addRect(
+            Rect(
+                top = 0f,
+                bottom = 0.5f * HANDLE_HEIGHT.value,
+                left = 0f,
+                right = 0.5f * HANDLE_WIDTH.value
+            )
+        )
+        path.addOval(
+            Rect(
+                top = 0f,
+                bottom = HANDLE_HEIGHT.value,
+                left = 0f,
+                right = HANDLE_WIDTH.value
+            )
+        )
+
+        canvas.drawPath(path, paint)
+    }
+}
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionManager.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionManager.kt
index 53c9ef5..d7edee4 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionManager.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionManager.kt
@@ -21,7 +21,6 @@
 import androidx.ui.core.PxPosition
 import androidx.ui.core.gesture.DragObserver
 import androidx.ui.core.px
-import androidx.ui.engine.geometry.Rect
 
 internal class SelectionManager : SelectionRegistrar {
     /**
@@ -65,6 +64,14 @@
     private var dragTotalDistance = PxPosition.Origin
 
     /**
+     * A flag to check if the selection start or end handle is being dragged.
+     * If this value is true, then onPress will not select any text.
+     * This value will be set to true when either handle is being dragged, and be reset to false
+     * when the dragging is stopped.
+     */
+    private var draggingHandle = false
+
+    /**
      * Allow a Text composable to "register" itself with the manager
      */
     override fun subscribe(handler: TextSelectionHandler): Any {
@@ -80,6 +87,7 @@
     }
 
     fun onPress(position: PxPosition) {
+        if (draggingHandle) return
         var result: Selection? = null
         for (handler in handlers) {
             result += handler.getSelection(
@@ -90,11 +98,15 @@
         onSelectionChange(result)
     }
 
-    // Get the coordinates of a character. Currently, it's the middle point of the left edge of the
-    // bounding box of the character. This is a temporary solution.
-    // TODO(qqd): Read how Android solve this problem.
-    fun getCoordinatesForCharacter(box: Rect): PxPosition {
-        return PxPosition(box.left.px, box.top.px + (box.bottom.px - box.top.px) / 2)
+    /**
+     * Adjust coordinates for given text offset.
+     *
+     * Currently [android.text.Layout.getLineBottom] returns y coordinates of the next
+     * line's top offset, which is not included in current line's hit area. To be able to
+     * hit current line, move up this y coordinates by 1 pixel.
+     */
+    fun getAdjustedCoordinates(p: PxPosition): PxPosition {
+        return PxPosition(p.x, p.y - 1.px)
     }
 
     fun handleDragObserver(dragStartHandle: Boolean): DragObserver {
@@ -112,11 +124,11 @@
                 // The position of the character where the drag gesture should begin. This is in
                 // the widget coordinates.
                 val beginCoordinates =
-                    getCoordinatesForCharacter(
+                    getAdjustedCoordinates(
                         if (dragStartHandle) {
-                            selection!!.startOffset
+                            selection!!.startCoordinates
                         } else {
-                            selection!!.endOffset
+                            selection!!.endCoordinates
                         }
                     )
                 // Convert the position where drag gesture begins from widget coordinates to
@@ -128,6 +140,7 @@
 
                 // Zero out the total distance that being dragged.
                 dragTotalDistance = PxPosition.Origin
+                draggingHandle = true
             }
 
             override fun onDrag(dragDistance: PxPosition): PxPosition {
@@ -140,7 +153,7 @@
                     } else {
                         containerLayoutCoordinates.childToLocal(
                             selection!!.startLayoutCoordinates!!,
-                            getCoordinatesForCharacter(selection!!.startOffset)
+                            getAdjustedCoordinates(selection!!.startCoordinates)
                         )
                     }
 
@@ -148,7 +161,7 @@
                     if (dragStartHandle) {
                         containerLayoutCoordinates.childToLocal(
                             selection!!.endLayoutCoordinates!!,
-                            getCoordinatesForCharacter(selection!!.endOffset)
+                            getAdjustedCoordinates(selection!!.endCoordinates)
                         )
                     } else {
                         dragBeginPosition + dragTotalDistance
@@ -163,6 +176,11 @@
                 onSelectionChange(result)
                 return dragDistance
             }
+
+            override fun onStop(velocity: PxPosition) {
+                super.onStop(velocity)
+                draggingHandle = false
+            }
         }
     }
 }
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/TextSelectionProcessor.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/TextSelectionProcessor.kt
index 23604f0..23419ed 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/TextSelectionProcessor.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/TextSelectionProcessor.kt
@@ -18,8 +18,6 @@
 
 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.text.TextSelection
 import androidx.ui.text.TextPainter
 import kotlin.math.max
@@ -39,19 +37,24 @@
     /** The TextPainter object from Text widget. */
     val textPainter: TextPainter
 ) {
-    // TODO(qqd): Determine a set of coordinates around a character that we need.
     /**
-     * The bounding box of the character at the start offset as Rect. The bounding box includes the
-     * top, bottom, left, and right of the character. Note: It is temporary to use Rect.
+     * The coordinates of the graphical position for selection start character offset.
+     *
+     * This graphical position is the point at the left bottom corner for LTR
+     * character, or right bottom corner for RTL character.
+     *
+     * This coordinates is in child widget coordinates system.
      */
-    // TODO(qqd): After solving the problem of getting the coordinates of a character, figure out
-    // what should the startOffset and endOffset should be.
-    internal var startOffset = Rect.zero
+    internal var startCoordinates: PxPosition = PxPosition.Origin
     /**
-     * The bounding box of the character at the end offset as Rect. The bounding box includes the
-     * top, bottom, left, and right of the character. Note: It is temporary to use Rect.
+     * The coordinates of the graphical position for selection end character offset.
+     *
+     * This graphical position is the point at the left bottom corner for LTR
+     * character, or right bottom corner for RTL character.
+     *
+     * This coordinates is in child widget coordinates system.
      */
-    internal var endOffset = Rect.zero
+    internal var endCoordinates: PxPosition = PxPosition.Origin
     /**
      * A flag to check if the text widget contains the whole selection's start.
      */
@@ -94,22 +97,12 @@
             val wordBoundary = textPainter.getWordBoundary(textSelectionStart)
             textSelectionStart = wordBoundary.start
             textSelectionEnd = wordBoundary.end
-        } else {
-            // Currently the implementation of selection is inclusive-inclusive which is a temporary
-            // workaround, but inclusive-exclusive in Android. Thus before calling drawing selection
-            // background, make the selection matches Android behaviour.
-            textSelectionEnd = textSelectionEnd + 1
         }
 
         onSelectionChange(TextSelection(textSelectionStart, textSelectionEnd))
 
-        // Currently the implementation of selection is inclusive-inclusive which is a temporary
-        // workaround, but inclusive-exclusive in Android. Thus make the selection end matches Crane
-        // behaviour.
-        textSelectionEnd = textSelectionEnd - 1
-
-        startOffset = textPainter.getBoundingBox(textSelectionStart)
-        endOffset = textPainter.getBoundingBox(textSelectionEnd)
+        startCoordinates = getSelectionHandleCoordinates(textSelectionStart)
+        endCoordinates = getSelectionHandleCoordinates(textSelectionEnd)
 
         this.containsWholeSelectionStart = containsWholeSelectionStart
         this.containsWholeSelectionEnd = containsWholeSelectionEnd
@@ -125,10 +118,10 @@
         position: PxPosition,
         isStart: Boolean
     ): Pair<Int, Boolean> {
-        // The text position of the border of selection. The default value is set to the beginning
-        // of the text widget for the start border, and the very last position of the text widget
-        // for the end border. If the widget contains the whole selection's border, this value will
-        // be reset.
+        // The character offset of the border of selection. The default value is set to the
+        // beginning of the text widget for the start border, and the very last character offset
+        // of the text widget for the end border. If the widget contains the whole selection's
+        // border, this value will be reset.
         var selectionBorder = if (isStart) 0 else max(length - 1, 0)
         // Flag to check if the widget contains the whole selection's border.
         var containsWholeSelectionBorder = false
@@ -138,21 +131,29 @@
         val left = 0.px
         val right = textPainter.width.px
         // If the current text widget contains the whole selection's border, then find the exact
-        // text position of the border, and the flag checking  if the widget contains the whole
+        // character offset of the border, and the flag checking if the widget contains the whole
         // selection's border will be set to true.
         if (position.x >= left &&
             position.x < right &&
             position.y >= top &&
             position.y < bottom
         ) {
-            val offset = Offset(position.x.value, position.y.value)
-            // Constrain the position of the selection border to be within the text range of the
-            // current widget.
-            val constrainedSelectionBorderPosition =
-                textPainter.getPositionForOffset(offset).coerceIn(0, length - 1)
-            selectionBorder = constrainedSelectionBorderPosition
+            // Constrain the character offset of the selection border to be within the text range
+            // of the current widget.
+            val constrainedSelectionBorderOffset =
+                textPainter.getOffsetForPosition(position).coerceIn(0, length - 1)
+            selectionBorder = constrainedSelectionBorderOffset
             containsWholeSelectionBorder = true
         }
         return Pair(selectionBorder, containsWholeSelectionBorder)
     }
+
+    private fun getSelectionHandleCoordinates(offset: Int): PxPosition {
+        val left = textPainter.getPrimaryHorizontal(offset)
+
+        val line = textPainter.getLineForOffset(offset)
+        val bottom = textPainter.getLineBottom(line)
+
+        return PxPosition(left.px, bottom.px)
+    }
 }
diff --git a/ui/ui-framework/src/test/java/androidx/ui/core/InputFieldDelegateTest.kt b/ui/ui-framework/src/test/java/androidx/ui/core/InputFieldDelegateTest.kt
index 595f498..f934c88 100644
--- a/ui/ui-framework/src/test/java/androidx/ui/core/InputFieldDelegateTest.kt
+++ b/ui/ui-framework/src/test/java/androidx/ui/core/InputFieldDelegateTest.kt
@@ -59,6 +59,37 @@
     private lateinit var textInputService: TextInputService
     private lateinit var layoutCoordinates: LayoutCoordinates
 
+    val creditCardOffsetTranslator = object : OffsetMap {
+        override fun originalToTransformed(offset: Int): Int {
+            if (offset <= 3) return offset
+            if (offset <= 7) return offset + 1
+            if (offset <= 11) return offset + 2
+            if (offset <= 16) return offset + 3
+            return 19
+        }
+
+        override fun transformedToOriginal(offset: Int): Int {
+            if (offset <= 4) return offset
+            if (offset <= 9) return offset - 1
+            if (offset <= 14) return offset - 2
+            if (offset <= 19) return offset - 3
+            return 16
+        }
+    }
+
+    private val identityOffsetMap = object : OffsetMap {
+        override fun originalToTransformed(offset: Int): Int = offset
+        override fun transformedToOriginal(offset: Int): Int = offset
+    }
+
+    /**
+     * Test implementation of offset map which doubles the offset in transformed text.
+     */
+    private val skippingOffsetMap = object : OffsetMap {
+        override fun originalToTransformed(offset: Int): Int = offset * 2
+        override fun transformedToOriginal(offset: Int): Int = offset / 2
+    }
+
     @Before
     fun setup() {
         painter = mock()
@@ -80,11 +111,13 @@
             textPainter = painter,
             value = EditorState(text = "Hello, World", selection = selection),
             editorStyle = EditorStyle(selectionColor = selectionColor),
-            hasFocus = true)
+            hasFocus = true,
+            offsetMap = identityOffsetMap
+        )
 
         verify(painter, times(1)).paintBackground(
-            eq(selection.start), eq(selection.end), eq(selectionColor), eq(canvas), any())
-        verify(painter, times(1)).paint(eq(canvas), any())
+            eq(selection.start), eq(selection.end), eq(selectionColor), eq(canvas))
+        verify(painter, times(1)).paint(eq(canvas))
 
         verify(painter, never()).paintCursor(any(), any())
     }
@@ -98,11 +131,13 @@
             textPainter = painter,
             value = EditorState(text = "Hello, World", selection = cursor),
             editorStyle = EditorStyle(),
-            hasFocus = true)
+            hasFocus = true,
+            offsetMap = identityOffsetMap
+        )
 
         verify(painter, times(1)).paintCursor(eq(cursor.start), eq(canvas))
-        verify(painter, times(1)).paint(eq(canvas), any())
-        verify(painter, never()).paintBackground(any(), any(), any(), any(), any())
+        verify(painter, times(1)).paint(eq(canvas))
+        verify(painter, never()).paintBackground(any(), any(), any(), any())
     }
 
     @Test
@@ -114,11 +149,13 @@
             textPainter = painter,
             value = EditorState(text = "Hello, World", selection = cursor),
             editorStyle = EditorStyle(),
-            hasFocus = false)
+            hasFocus = false,
+            offsetMap = identityOffsetMap
+        )
 
         verify(painter, never()).paintCursor(any(), any())
-        verify(painter, times(1)).paint(eq(canvas), any())
-        verify(painter, never()).paintBackground(any(), any(), any(), any(), any())
+        verify(painter, times(1)).paint(eq(canvas))
+        verify(painter, never()).paintBackground(any(), any(), any(), any())
     }
 
     @Test
@@ -134,11 +171,13 @@
             value = EditorState(text = "Hello, World", selection = cursor,
                 composition = composition),
             editorStyle = EditorStyle(compositionColor = compositionColor),
-            hasFocus = true)
+            hasFocus = true,
+            offsetMap = identityOffsetMap
+        )
 
         verify(painter, times(1)).paintBackground(
-            eq(composition.start), eq(composition.end), eq(compositionColor), eq(canvas), any())
-        verify(painter, times(1)).paint(eq(canvas), any())
+            eq(composition.start), eq(composition.end), eq(compositionColor), eq(canvas))
+        verify(painter, times(1)).paint(eq(canvas))
         verify(painter, times(1)).paintCursor(eq(cursor.start), any())
     }
 
@@ -160,18 +199,45 @@
         val offset = 10
         val dummyEditorState = EditorState(text = "Hello, World", selection = TextRange(1, 1))
 
-        whenever(painter.getPositionForOffset(position.toOffset())).thenReturn(offset)
+        whenever(painter.getOffsetForPosition(position)).thenReturn(offset)
 
         val captor = argumentCaptor<List<EditOperation>>()
 
         whenever(processor.onEditCommands(captor.capture())).thenReturn(dummyEditorState)
 
-        InputFieldDelegate.onRelease(position, painter, processor, onValueChange)
+        InputFieldDelegate.onRelease(
+            position,
+            painter,
+            processor,
+            identityOffsetMap,
+            onValueChange,
+            textInputService,
+            true)
 
         assertEquals(1, captor.allValues.size)
         assertEquals(1, captor.firstValue.size)
         assertTrue(captor.firstValue[0] is SetSelectionEditOp)
         verify(onValueChange, times(1)).invoke(eq(dummyEditorState))
+        verify(textInputService).showSoftwareKeyboard()
+    }
+
+    @Test
+    fun test_on_release_do_not_place_cursor_if_focus_is_out() {
+        val position = PxPosition(100.px, 200.px)
+        val offset = 10
+
+        whenever(painter.getOffsetForPosition(position)).thenReturn(offset)
+        InputFieldDelegate.onRelease(
+            position,
+            painter,
+            processor,
+            identityOffsetMap,
+            onValueChange,
+            textInputService,
+            false)
+
+        verify(onValueChange, never()).invoke(any())
+        verify(textInputService).showSoftwareKeyboard()
     }
 
     @Test
@@ -186,22 +252,17 @@
                 composition = TextRange(1, 3)
             ),
             editorStyle = EditorStyle(compositionColor = Color.Red),
-            hasFocus = true
-        )
+            hasFocus = true,
+            offsetMap = identityOffsetMap
+            )
 
         inOrder(painter) {
-            verify(painter).paintBackground(eq(1), eq(3), eq(Color.Red), eq(canvas), any())
+            verify(painter).paintBackground(eq(1), eq(3), eq(Color.Red), eq(canvas))
             verify(painter).paintCursor(eq(1), eq(canvas))
         }
     }
 
     @Test
-    fun show_soft_input() {
-        InputFieldDelegate.onPress(textInputService)
-        verify(textInputService).showSoftwareKeyboard()
-    }
-
-    @Test
     fun on_focus() {
         val dummyEditorState = EditorState(text = "Hello, World", selection = TextRange(1, 1))
         InputFieldDelegate.onFocus(textInputService, dummyEditorState, processor,
@@ -241,7 +302,9 @@
             painter,
             layoutCoordinates,
             textInputService,
-            true /* hasFocus */)
+            true /* hasFocus */,
+            identityOffsetMap
+        )
         verify(textInputService).notifyFocusedRect(any())
     }
 
@@ -253,7 +316,9 @@
             painter,
             layoutCoordinates,
             textInputService,
-            false /* hasFocus */)
+            false /* hasFocus */,
+            identityOffsetMap
+        )
         verify(textInputService, never()).notifyFocusedRect(any())
     }
 
@@ -269,14 +334,16 @@
             painter,
             layoutCoordinates,
             textInputService,
-            true /* hasFocus */)
+            true /* hasFocus */,
+            identityOffsetMap
+        )
         verify(textInputService).notifyFocusedRect(any())
     }
 
     @Test
     fun notify_rect_empty() {
-        val dummyRect = Rect(0f, 1f, 2f, 3f)
-        whenever(painter.getBoundingBox(any())).thenReturn(dummyRect)
+        val dummyHeight = 64f
+        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))
@@ -285,8 +352,11 @@
             painter,
             layoutCoordinates,
             textInputService,
-            true /* hasFocus */)
-        verify(textInputService).notifyFocusedRect(any())
+            true, /* hasFocus */
+            identityOffsetMap)
+        val captor = argumentCaptor<Rect>()
+        verify(textInputService).notifyFocusedRect(captor.capture())
+        assertEquals(dummyHeight, captor.firstValue.height)
     }
 
     @Test
@@ -311,4 +381,93 @@
 
         verify(painter, times(1)).layout(constraints)
     }
+
+    @Test
+    fun check_draw_uses_offset_map() {
+        val selection = TextRange(1, 3)
+        val selectionColor = Color.Blue
+
+        InputFieldDelegate.draw(
+            canvas = canvas,
+            textPainter = painter,
+            value = EditorState(text = "Hello, World", selection = selection),
+            editorStyle = EditorStyle(selectionColor = selectionColor),
+            hasFocus = true,
+            offsetMap = skippingOffsetMap
+        )
+
+        val selectionStartInTransformedText = selection.start * 2
+        val selectionEmdInTransformedText = selection.end * 2
+
+        verify(painter, times(1)).paintBackground(
+            eq(selectionStartInTransformedText),
+            eq(selectionEmdInTransformedText),
+            eq(selectionColor),
+            eq(canvas))
+    }
+
+    @Test
+    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))
+        whenever(painter.getBoundingBox(any())).thenReturn(dummyRect)
+        whenever(layoutCoordinates.localToRoot(any())).thenReturn(dummyPoint)
+
+        InputFieldDelegate.notifyFocusedRect(
+            dummyEditorState,
+            painter,
+            layoutCoordinates,
+            textInputService,
+            true /* hasFocus */,
+            skippingOffsetMap
+        )
+        verify(painter).getBoundingBox(6)
+        verify(textInputService).notifyFocusedRect(any())
+    }
+
+    @Test
+    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))
+
+        whenever(painter.getOffsetForPosition(position)).thenReturn(offset)
+
+        val captor = argumentCaptor<List<EditOperation>>()
+
+        whenever(processor.onEditCommands(captor.capture())).thenReturn(dummyEditorState)
+
+        InputFieldDelegate.onRelease(
+            position,
+            painter,
+            processor,
+            skippingOffsetMap,
+            onValueChange,
+            textInputService,
+            true)
+
+        val cursorOffsetInTransformedText = offset / 2
+        assertEquals(1, captor.allValues.size)
+        assertEquals(1, captor.firstValue.size)
+        assertTrue(captor.firstValue[0] is SetSelectionEditOp)
+        val setSelectionEditOp = captor.firstValue[0] as SetSelectionEditOp
+        assertEquals(cursorOffsetInTransformedText, setSelectionEditOp.start)
+        assertEquals(cursorOffsetInTransformedText, setSelectionEditOp.end)
+        verify(onValueChange, times(1)).invoke(eq(dummyEditorState))
+    }
+
+    @Test
+    fun use_identity_mapping_if_visual_transformation_is_null() {
+        val (visualText, offsetMap) = InputFieldDelegate.applyVisualFilter(
+            EditorState(text = "Hello, World"),
+            null)
+
+        assertEquals("Hello, World", visualText.text)
+        for (i in 0..visualText.text.length) {
+            // Identity mapping returns if no visual filter is provided.
+            assertEquals(i, offsetMap.originalToTransformed(i))
+            assertEquals(i, offsetMap.transformedToOriginal(i))
+        }
+    }
 }
diff --git a/ui/ui-framework/src/test/java/androidx/ui/core/PasswordVisualTransformationTest.kt b/ui/ui-framework/src/test/java/androidx/ui/core/PasswordVisualTransformationTest.kt
new file mode 100644
index 0000000..1284040
--- /dev/null
+++ b/ui/ui-framework/src/test/java/androidx/ui/core/PasswordVisualTransformationTest.kt
@@ -0,0 +1,52 @@
+/*
+ * 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
+
+import androidx.ui.text.AnnotatedString
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class PasswordVisualTransformationTest {
+    @Test
+    fun check_visual_output_is_masked_with_asterisk() {
+        val transformation = PasswordVisualTransformation(mask = '*')
+        val text = AnnotatedString("12345")
+        val (transformedText, map) = transformation.filter(text)
+
+        assertEquals("*****", transformedText.text)
+        for (i in 0..transformedText.text.length) {
+            assertEquals(i, map.originalToTransformed(i))
+            assertEquals(i, map.transformedToOriginal(i))
+        }
+    }
+
+    @Test
+    fun check_visual_output_is_masked_with_default() {
+        val filter = PasswordVisualTransformation()
+        val text = AnnotatedString("1234567890")
+        val (filtered, map) = filter.filter(text)
+
+        assertEquals("\u2022".repeat(10), filtered.text)
+        for (i in 0..filtered.text.length) {
+            assertEquals(i, map.originalToTransformed(i))
+            assertEquals(i, map.transformedToOriginal(i))
+        }
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-framework/src/test/java/androidx/ui/core/gesture/LongPressGestureDetectorTest.kt b/ui/ui-framework/src/test/java/androidx/ui/core/gesture/LongPressGestureDetectorTest.kt
new file mode 100644
index 0000000..45c5d9f
--- /dev/null
+++ b/ui/ui-framework/src/test/java/androidx/ui/core/gesture/LongPressGestureDetectorTest.kt
@@ -0,0 +1,403 @@
+/*
+ * 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.gesture
+
+import androidx.ui.core.PointerEventPass
+import androidx.ui.core.PxPosition
+import androidx.ui.core.consumeDownChange
+import androidx.ui.core.milliseconds
+import androidx.ui.core.millisecondsToTimestamp
+import androidx.ui.core.px
+import androidx.ui.testutils.consume
+import androidx.ui.testutils.down
+import androidx.ui.testutils.invokeOverAllPasses
+import androidx.ui.testutils.moveBy
+import androidx.ui.testutils.moveTo
+import androidx.ui.testutils.up
+import com.google.common.truth.Truth.assertThat
+import com.nhaarman.mockitokotlin2.any
+import com.nhaarman.mockitokotlin2.mock
+import com.nhaarman.mockitokotlin2.never
+import com.nhaarman.mockitokotlin2.verify
+import kotlinx.coroutines.ObsoleteCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineContext
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import java.util.concurrent.TimeUnit
+
+@ObsoleteCoroutinesApi
+@RunWith(JUnit4::class)
+class LongPressGestureDetectorTest {
+
+    private val LongPressTimeoutMillis = 100.milliseconds
+    private val testContext = TestCoroutineContext()
+    private val listener: (PxPosition) -> Unit = mock()
+    private lateinit var mRecognizer: LongPressGestureRecognizer
+
+    @Before
+    fun setup() {
+        mRecognizer = LongPressGestureRecognizer(listener, testContext)
+        mRecognizer.longPressTimeout = LongPressTimeoutMillis
+    }
+
+    // Tests that verify conditions under which onLongPress will not be called.
+
+    @Test
+    fun pointerInputHandler_down_onLongPressNotCalled() {
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down()))
+        verify(mRecognizer.onLongPress, never()).invoke(any())
+    }
+
+    @Test
+    fun pointerInputHandler_downWithinTimeout_onLongPressNotCalled() {
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down()))
+        testContext.advanceTimeBy(99, TimeUnit.MILLISECONDS)
+        verify(mRecognizer.onLongPress, never()).invoke(any())
+    }
+
+    @Test
+    fun pointerInputHandler_DownMoveConsumed_onLongPressNotCalled() {
+        val down = down(0)
+        val move = down.moveBy(50.milliseconds, 1f, 1f).consume(1f, 0f)
+
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(move))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+
+        verify(mRecognizer.onLongPress, never()).invoke(any())
+    }
+
+    @Test
+    fun pointerInputHandler_2Down1MoveConsumed_onLongPressNotCalled() {
+        val down0 = down(0)
+        val down1 = down(1)
+        val move0 = down0.moveBy(50.milliseconds, 1f, 1f).consume(1f, 0f)
+        val move1 = down0.moveBy(50.milliseconds, 0f, 0f)
+
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down0, down1))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(move0, move1))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+
+        verify(mRecognizer.onLongPress, never()).invoke(any())
+    }
+
+    @Test
+    fun pointerInputHandler_DownUpConsumed_onLongPressNotCalled() {
+        val down = down(0)
+        val up = down.up(50L.millisecondsToTimestamp()).consumeDownChange()
+
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(up))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+
+        verify(mRecognizer.onLongPress, never()).invoke(any())
+    }
+
+    @Test
+    fun pointerInputHandler_DownUpNotConsumed_onLongPressNotCalled() {
+        val down = down(0)
+        val up = down.up(50L.millisecondsToTimestamp())
+
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(up))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+
+        verify(mRecognizer.onLongPress, never()).invoke(any())
+    }
+
+    @Test
+    fun pointerInputHandler_2DownIndependentlyUnderTimeoutAndDoNotOverlap_onLongPressNotCalled() {
+
+        // Arrange
+
+        val down0 = down(0)
+
+        val up0 = down0.up(50L.millisecondsToTimestamp())
+
+        val down1 = down(1, 51L.millisecondsToTimestamp())
+
+        // Act
+
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(
+            down0
+        ))
+
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(
+            up0
+        ))
+
+        testContext.advanceTimeBy(1, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(
+            down1
+        ))
+
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+
+        // Assert
+
+        verify(mRecognizer.onLongPress, never()).invoke(any())
+    }
+
+    // Tests that verify conditions under which onLongPress will be called.
+
+    @Test
+    fun pointerInputHandler_downBeyondTimeout_onLongPressCalled() {
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down()))
+        testContext.advanceTimeBy(100, TimeUnit.MILLISECONDS)
+        verify(mRecognizer.onLongPress).invoke(any())
+    }
+
+    @Test
+    fun pointerInputHandler_2DownBeyondTimeout_onLongPressCalled() {
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down(0), down(1)))
+        testContext.advanceTimeBy(100, TimeUnit.MILLISECONDS)
+        verify(mRecognizer.onLongPress).invoke(any())
+    }
+
+    @Test
+    fun pointerInputHandler_2DownIndependentlyUnderTimeoutButOverlapTimeIsOver_onLongPressCalled() {
+
+        // Arrange
+
+        val down0 = down(0)
+
+        val move0 = down0.moveTo(50L.millisecondsToTimestamp(), 0f, 0f)
+        val down1 = down(1, 50L.millisecondsToTimestamp())
+
+        val up0 = move0.up(75L.millisecondsToTimestamp())
+        val move1 = down1.moveTo(75L.millisecondsToTimestamp(), 0f, 0f)
+
+        // Act
+
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(
+            down0
+        ))
+
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(
+            move0, down1
+        ))
+
+        testContext.advanceTimeBy(25, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(
+            up0, move1
+        ))
+
+        testContext.advanceTimeBy(25, TimeUnit.MILLISECONDS)
+
+        // Assert
+
+        verify(mRecognizer.onLongPress).invoke(any())
+    }
+
+    @Test
+    fun pointerInputHandler_downMoveNotConsumed_onLongPressCalled() {
+        val down = down(0)
+        val move = down.moveBy(50.milliseconds, 1f, 1f)
+
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(move))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+
+        verify(mRecognizer.onLongPress).invoke(any())
+    }
+
+    // Tests that verify correctness of PxPosition value passed to onLongPress
+
+    @Test
+    fun pointerInputHandler_down_onLongPressCalledWithDownPosition() {
+        val down = down(0, x = 13f, y = 17f)
+
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down))
+        testContext.advanceTimeBy(100, TimeUnit.MILLISECONDS)
+
+        verify(mRecognizer.onLongPress).invoke(PxPosition(13.px, 17.px))
+    }
+
+    @Test
+    fun pointerInputHandler_downMove_onLongPressCalledWithMovePosition() {
+        val down = down(0, x = 13f, y = 17f)
+        val move = down.moveTo(50L.millisecondsToTimestamp(), -7f, 5f)
+
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(move))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+
+        verify(mRecognizer.onLongPress).invoke(PxPosition((-7).px, 5.px))
+    }
+
+    @Test
+    fun pointerInputHandler_downThenDown_onLongPressCalledWithFirstDownPosition() {
+        val down0 = down(0, x = 13f, y = 17f)
+
+        val move0 = down0.moveBy(50.milliseconds, 0f, 0f)
+        val down1 = down(1, 50L.millisecondsToTimestamp(), x = 11f, y = 19f)
+
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down0))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(move0, down1))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+
+        verify(mRecognizer.onLongPress).invoke(PxPosition(13.px, 17.px))
+    }
+
+    @Test
+    fun pointerInputHandler_down0ThenDown1ThenUp0_onLongPressCalledWithDown1Position() {
+        val down0 = down(0, x = 13f, y = 17f)
+
+        val move0 = down0.moveTo(50L.millisecondsToTimestamp(), 27f, 29f)
+        val down1 = down(1, 50L.millisecondsToTimestamp(), x = 11f, y = 19f)
+
+        val up0 = move0.up(75L.millisecondsToTimestamp())
+        val move1 = down1.moveBy(25.milliseconds, 0f, 0f)
+
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down0))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(move0, down1))
+        testContext.advanceTimeBy(25, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(up0, move1))
+        testContext.advanceTimeBy(25, TimeUnit.MILLISECONDS)
+
+        verify(mRecognizer.onLongPress).invoke(PxPosition(11.px, 19.px))
+    }
+
+    @Test
+    fun pointerInputHandler_down0ThenMove0AndDown1_onLongPressCalledWithMove0Position() {
+        val down0 = down(0, x = 13f, y = 17f)
+
+        val move0 = down0.moveTo(50L.millisecondsToTimestamp(), 27f, 29f)
+        val down1 = down(1, 50L.millisecondsToTimestamp(), x = 11f, y = 19f)
+
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down0))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(move0, down1))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+
+        verify(mRecognizer.onLongPress).invoke(PxPosition(27.px, 29.px))
+    }
+
+    @Test
+    fun pointerInputHandler_down0Down1Move1Up0_onLongPressCalledWithMove1Position() {
+        val down0 = down(0, x = 13f, y = 17f)
+
+        val move0 = down0.moveBy(25.milliseconds, 0f, 0f)
+        val down1 = down(1, 25L.millisecondsToTimestamp(), x = 11f, y = 19f)
+
+        val up0 = move0.up(50L.millisecondsToTimestamp())
+        val move1 = down1.moveTo(50L.millisecondsToTimestamp(), 27f, 23f)
+
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(down0))
+        testContext.advanceTimeBy(25, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(move0, down1))
+        testContext.advanceTimeBy(25, TimeUnit.MILLISECONDS)
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(up0, move1))
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+
+        verify(mRecognizer.onLongPress).invoke(PxPosition(27.px, 23.px))
+    }
+
+    // Tests that verify that consumption behavior
+
+    @Test
+    fun pointerInputHandler_1Down_notConsumed() {
+        val down0 = down(0)
+        val result = mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(
+            down0
+        ))
+        assertThat(result[0].consumed.downChange).isFalse()
+    }
+
+    @Test
+    fun pointerInputHandler_1DownThen1Down_notConsumed() {
+
+        // Arrange
+
+        val down0 = down(0, 0L.millisecondsToTimestamp())
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(
+            down0
+        ))
+
+        // Act
+
+        testContext.advanceTimeBy(10, TimeUnit.MILLISECONDS)
+        val move0 = down0.moveTo(10L.millisecondsToTimestamp(), 0f, 0f)
+        val down1 = down(0, 10L.millisecondsToTimestamp())
+        val result = mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(
+            move0, down1
+        ))
+
+        // Assert
+
+        assertThat(result[0].consumed.downChange).isFalse()
+        assertThat(result[1].consumed.downChange).isFalse()
+    }
+
+    @Test
+    fun pointerInputHandler_1DownUnderTimeUp_upNotConsumed() {
+
+        // Arrange
+
+        val down0 = down(0, 0L.millisecondsToTimestamp())
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(
+            down0
+        ))
+
+        // Act
+
+        testContext.advanceTimeBy(50, TimeUnit.MILLISECONDS)
+        val up0 = down0.up(50L.millisecondsToTimestamp())
+        val result = mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(
+            up0
+        ))
+
+        // Assert
+
+        assertThat(result[0].consumed.downChange).isFalse()
+    }
+
+    @Test
+    fun pointerInputHandler_1DownUOverTimeUp_upConsumedOnInitialDown() {
+
+        // Arrange
+
+        val down0 = down(0, 0L.millisecondsToTimestamp())
+        mRecognizer.pointerInputHandler.invokeOverAllPasses(listOf(
+            down0
+        ))
+
+        // Act
+
+        testContext.advanceTimeBy(101, TimeUnit.MILLISECONDS)
+        val up0 = down0.up(100L.millisecondsToTimestamp())
+        val result = mRecognizer.pointerInputHandler.invoke(listOf(
+            up0
+        ), PointerEventPass.InitialDown)
+
+        // Assert
+
+        assertThat(result[0].consumed.downChange).isTrue()
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-layout/api/1.0.0-alpha01.txt b/ui/ui-layout/api/1.0.0-alpha01.txt
index c19b96d..fe4723d 100644
--- a/ui/ui-layout/api/1.0.0-alpha01.txt
+++ b/ui/ui-layout/api/1.0.0-alpha01.txt
@@ -182,23 +182,36 @@
   public abstract sealed class TableColumnWidth {
   }
 
-  public static final class TableColumnWidth.Fixed extends androidx.ui.layout.TableColumnWidth {
-    ctor public TableColumnWidth.Fixed(internal androidx.ui.core.Dp width);
-    method public androidx.ui.layout.TableColumnWidth.Fixed copy(androidx.ui.core.Dp width);
+  public static final class TableColumnWidth.Flexible extends androidx.ui.layout.TableColumnWidth {
+    ctor public TableColumnWidth.Flexible(internal float flex);
+    method public androidx.ui.layout.TableColumnWidth.Flexible copy(float flex);
   }
 
-  public static final class TableColumnWidth.Flex extends androidx.ui.layout.TableColumnWidth {
-    ctor public TableColumnWidth.Flex(internal float flex);
-    method public androidx.ui.layout.TableColumnWidth.Flex copy(float flex);
+  public abstract static sealed class TableColumnWidth.Inflexible extends androidx.ui.layout.TableColumnWidth {
   }
 
-  public static final class TableColumnWidth.Fraction extends androidx.ui.layout.TableColumnWidth {
-    ctor public TableColumnWidth.Fraction(@FloatRange(from=null, to=null) internal float fraction);
-    method public androidx.ui.layout.TableColumnWidth.Fraction copy(float fraction);
+  public static final class TableColumnWidth.Inflexible.Fixed extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Fixed(internal androidx.ui.core.Dp width);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Fixed copy(androidx.ui.core.Dp width);
   }
 
-  public static final class TableColumnWidth.Wrap extends androidx.ui.layout.TableColumnWidth {
-    field public static final androidx.ui.layout.TableColumnWidth.Wrap! INSTANCE;
+  public static final class TableColumnWidth.Inflexible.Fraction extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Fraction(@FloatRange(from=null, to=null) internal float fraction);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Fraction copy(float fraction);
+  }
+
+  public static final class TableColumnWidth.Inflexible.Max extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Max(internal androidx.ui.layout.TableColumnWidth.Inflexible a, internal androidx.ui.layout.TableColumnWidth.Inflexible b);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Max copy(androidx.ui.layout.TableColumnWidth.Inflexible a, androidx.ui.layout.TableColumnWidth.Inflexible b);
+  }
+
+  public static final class TableColumnWidth.Inflexible.Min extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Min(internal androidx.ui.layout.TableColumnWidth.Inflexible a, internal androidx.ui.layout.TableColumnWidth.Inflexible b);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Min copy(androidx.ui.layout.TableColumnWidth.Inflexible a, androidx.ui.layout.TableColumnWidth.Inflexible b);
+  }
+
+  public static final class TableColumnWidth.Inflexible.Wrap extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    field public static final androidx.ui.layout.TableColumnWidth.Inflexible.Wrap! INSTANCE;
   }
 
   public final class TableKt {
diff --git a/ui/ui-layout/api/current.txt b/ui/ui-layout/api/current.txt
index c19b96d..fe4723d 100644
--- a/ui/ui-layout/api/current.txt
+++ b/ui/ui-layout/api/current.txt
@@ -182,23 +182,36 @@
   public abstract sealed class TableColumnWidth {
   }
 
-  public static final class TableColumnWidth.Fixed extends androidx.ui.layout.TableColumnWidth {
-    ctor public TableColumnWidth.Fixed(internal androidx.ui.core.Dp width);
-    method public androidx.ui.layout.TableColumnWidth.Fixed copy(androidx.ui.core.Dp width);
+  public static final class TableColumnWidth.Flexible extends androidx.ui.layout.TableColumnWidth {
+    ctor public TableColumnWidth.Flexible(internal float flex);
+    method public androidx.ui.layout.TableColumnWidth.Flexible copy(float flex);
   }
 
-  public static final class TableColumnWidth.Flex extends androidx.ui.layout.TableColumnWidth {
-    ctor public TableColumnWidth.Flex(internal float flex);
-    method public androidx.ui.layout.TableColumnWidth.Flex copy(float flex);
+  public abstract static sealed class TableColumnWidth.Inflexible extends androidx.ui.layout.TableColumnWidth {
   }
 
-  public static final class TableColumnWidth.Fraction extends androidx.ui.layout.TableColumnWidth {
-    ctor public TableColumnWidth.Fraction(@FloatRange(from=null, to=null) internal float fraction);
-    method public androidx.ui.layout.TableColumnWidth.Fraction copy(float fraction);
+  public static final class TableColumnWidth.Inflexible.Fixed extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Fixed(internal androidx.ui.core.Dp width);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Fixed copy(androidx.ui.core.Dp width);
   }
 
-  public static final class TableColumnWidth.Wrap extends androidx.ui.layout.TableColumnWidth {
-    field public static final androidx.ui.layout.TableColumnWidth.Wrap! INSTANCE;
+  public static final class TableColumnWidth.Inflexible.Fraction extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Fraction(@FloatRange(from=null, to=null) internal float fraction);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Fraction copy(float fraction);
+  }
+
+  public static final class TableColumnWidth.Inflexible.Max extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Max(internal androidx.ui.layout.TableColumnWidth.Inflexible a, internal androidx.ui.layout.TableColumnWidth.Inflexible b);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Max copy(androidx.ui.layout.TableColumnWidth.Inflexible a, androidx.ui.layout.TableColumnWidth.Inflexible b);
+  }
+
+  public static final class TableColumnWidth.Inflexible.Min extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Min(internal androidx.ui.layout.TableColumnWidth.Inflexible a, internal androidx.ui.layout.TableColumnWidth.Inflexible b);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Min copy(androidx.ui.layout.TableColumnWidth.Inflexible a, androidx.ui.layout.TableColumnWidth.Inflexible b);
+  }
+
+  public static final class TableColumnWidth.Inflexible.Wrap extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    field public static final androidx.ui.layout.TableColumnWidth.Inflexible.Wrap! INSTANCE;
   }
 
   public final class TableKt {
diff --git a/ui/ui-layout/api/restricted_1.0.0-alpha01.txt b/ui/ui-layout/api/restricted_1.0.0-alpha01.txt
index c19b96d..fe4723d 100644
--- a/ui/ui-layout/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-layout/api/restricted_1.0.0-alpha01.txt
@@ -182,23 +182,36 @@
   public abstract sealed class TableColumnWidth {
   }
 
-  public static final class TableColumnWidth.Fixed extends androidx.ui.layout.TableColumnWidth {
-    ctor public TableColumnWidth.Fixed(internal androidx.ui.core.Dp width);
-    method public androidx.ui.layout.TableColumnWidth.Fixed copy(androidx.ui.core.Dp width);
+  public static final class TableColumnWidth.Flexible extends androidx.ui.layout.TableColumnWidth {
+    ctor public TableColumnWidth.Flexible(internal float flex);
+    method public androidx.ui.layout.TableColumnWidth.Flexible copy(float flex);
   }
 
-  public static final class TableColumnWidth.Flex extends androidx.ui.layout.TableColumnWidth {
-    ctor public TableColumnWidth.Flex(internal float flex);
-    method public androidx.ui.layout.TableColumnWidth.Flex copy(float flex);
+  public abstract static sealed class TableColumnWidth.Inflexible extends androidx.ui.layout.TableColumnWidth {
   }
 
-  public static final class TableColumnWidth.Fraction extends androidx.ui.layout.TableColumnWidth {
-    ctor public TableColumnWidth.Fraction(@FloatRange(from=null, to=null) internal float fraction);
-    method public androidx.ui.layout.TableColumnWidth.Fraction copy(float fraction);
+  public static final class TableColumnWidth.Inflexible.Fixed extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Fixed(internal androidx.ui.core.Dp width);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Fixed copy(androidx.ui.core.Dp width);
   }
 
-  public static final class TableColumnWidth.Wrap extends androidx.ui.layout.TableColumnWidth {
-    field public static final androidx.ui.layout.TableColumnWidth.Wrap! INSTANCE;
+  public static final class TableColumnWidth.Inflexible.Fraction extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Fraction(@FloatRange(from=null, to=null) internal float fraction);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Fraction copy(float fraction);
+  }
+
+  public static final class TableColumnWidth.Inflexible.Max extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Max(internal androidx.ui.layout.TableColumnWidth.Inflexible a, internal androidx.ui.layout.TableColumnWidth.Inflexible b);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Max copy(androidx.ui.layout.TableColumnWidth.Inflexible a, androidx.ui.layout.TableColumnWidth.Inflexible b);
+  }
+
+  public static final class TableColumnWidth.Inflexible.Min extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Min(internal androidx.ui.layout.TableColumnWidth.Inflexible a, internal androidx.ui.layout.TableColumnWidth.Inflexible b);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Min copy(androidx.ui.layout.TableColumnWidth.Inflexible a, androidx.ui.layout.TableColumnWidth.Inflexible b);
+  }
+
+  public static final class TableColumnWidth.Inflexible.Wrap extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    field public static final androidx.ui.layout.TableColumnWidth.Inflexible.Wrap! INSTANCE;
   }
 
   public final class TableKt {
diff --git a/ui/ui-layout/api/restricted_current.txt b/ui/ui-layout/api/restricted_current.txt
index c19b96d..fe4723d 100644
--- a/ui/ui-layout/api/restricted_current.txt
+++ b/ui/ui-layout/api/restricted_current.txt
@@ -182,23 +182,36 @@
   public abstract sealed class TableColumnWidth {
   }
 
-  public static final class TableColumnWidth.Fixed extends androidx.ui.layout.TableColumnWidth {
-    ctor public TableColumnWidth.Fixed(internal androidx.ui.core.Dp width);
-    method public androidx.ui.layout.TableColumnWidth.Fixed copy(androidx.ui.core.Dp width);
+  public static final class TableColumnWidth.Flexible extends androidx.ui.layout.TableColumnWidth {
+    ctor public TableColumnWidth.Flexible(internal float flex);
+    method public androidx.ui.layout.TableColumnWidth.Flexible copy(float flex);
   }
 
-  public static final class TableColumnWidth.Flex extends androidx.ui.layout.TableColumnWidth {
-    ctor public TableColumnWidth.Flex(internal float flex);
-    method public androidx.ui.layout.TableColumnWidth.Flex copy(float flex);
+  public abstract static sealed class TableColumnWidth.Inflexible extends androidx.ui.layout.TableColumnWidth {
   }
 
-  public static final class TableColumnWidth.Fraction extends androidx.ui.layout.TableColumnWidth {
-    ctor public TableColumnWidth.Fraction(@FloatRange(from=null, to=null) internal float fraction);
-    method public androidx.ui.layout.TableColumnWidth.Fraction copy(float fraction);
+  public static final class TableColumnWidth.Inflexible.Fixed extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Fixed(internal androidx.ui.core.Dp width);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Fixed copy(androidx.ui.core.Dp width);
   }
 
-  public static final class TableColumnWidth.Wrap extends androidx.ui.layout.TableColumnWidth {
-    field public static final androidx.ui.layout.TableColumnWidth.Wrap! INSTANCE;
+  public static final class TableColumnWidth.Inflexible.Fraction extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Fraction(@FloatRange(from=null, to=null) internal float fraction);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Fraction copy(float fraction);
+  }
+
+  public static final class TableColumnWidth.Inflexible.Max extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Max(internal androidx.ui.layout.TableColumnWidth.Inflexible a, internal androidx.ui.layout.TableColumnWidth.Inflexible b);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Max copy(androidx.ui.layout.TableColumnWidth.Inflexible a, androidx.ui.layout.TableColumnWidth.Inflexible b);
+  }
+
+  public static final class TableColumnWidth.Inflexible.Min extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    ctor public TableColumnWidth.Inflexible.Min(internal androidx.ui.layout.TableColumnWidth.Inflexible a, internal androidx.ui.layout.TableColumnWidth.Inflexible b);
+    method public androidx.ui.layout.TableColumnWidth.Inflexible.Min copy(androidx.ui.layout.TableColumnWidth.Inflexible a, androidx.ui.layout.TableColumnWidth.Inflexible b);
+  }
+
+  public static final class TableColumnWidth.Inflexible.Wrap extends androidx.ui.layout.TableColumnWidth.Inflexible {
+    field public static final androidx.ui.layout.TableColumnWidth.Inflexible.Wrap! INSTANCE;
   }
 
   public final class TableKt {
diff --git a/ui/ui-layout/build.gradle b/ui/ui-layout/build.gradle
index 077ade1..2af0b7e 100644
--- a/ui/ui-layout/build.gradle
+++ b/ui/ui-layout/build.gradle
@@ -27,6 +27,7 @@
     id("com.android.library")
     id("AndroidXUiPlugin")
     id("org.jetbrains.kotlin.android")
+    id("androidx.benchmark")
 }
 
 dependencies {
@@ -44,7 +45,7 @@
     testImplementation(ANDROIDX_TEST_RUNNER)
     testImplementation(JUNIT)
 
-    androidTestImplementation project(":benchmark")
+    androidTestImplementation project(":benchmark:benchmark-junit4")
     androidTestImplementation project(":ui:ui-platform")
 
     androidTestImplementation(ANDROIDX_TEST_RULES)
diff --git a/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/TableSamples.kt b/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/TableSamples.kt
index 89c0fc0..fc5c908 100644
--- a/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/TableSamples.kt
+++ b/ui/ui-layout/integration-tests/samples/src/main/java/androidx/ui/layout/samples/TableSamples.kt
@@ -50,11 +50,11 @@
         Table(
             columnWidth = { columnIndex ->
                 when (columnIndex) {
-                    0 -> TableColumnWidth.Wrap
-                    1 -> TableColumnWidth.Flex(flex = 1f)
-                    2 -> TableColumnWidth.Flex(flex = 3f)
-                    3 -> TableColumnWidth.Fixed(width = 50.dp)
-                    else -> TableColumnWidth.Fraction(fraction = 0.5f)
+                    0 -> TableColumnWidth.Inflexible.Wrap
+                    1 -> TableColumnWidth.Flexible(flex = 1f)
+                    2 -> TableColumnWidth.Flexible(flex = 3f)
+                    3 -> TableColumnWidth.Inflexible.Fixed(width = 50.dp)
+                    else -> TableColumnWidth.Inflexible.Fraction(fraction = 0.5f)
                 }
             }
         ) {
diff --git a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/ScrollerPerformance.kt b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/ScrollerPerformance.kt
index 3efac80..652694d 100644
--- a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/ScrollerPerformance.kt
+++ b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/ScrollerPerformance.kt
@@ -17,8 +17,8 @@
 package androidx.ui.layout.test
 
 import android.view.View
-import androidx.benchmark.BenchmarkRule
-import androidx.benchmark.measureRepeated
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
 import androidx.compose.Composable
 import androidx.compose.CompositionContext
 import androidx.compose.FrameManager
diff --git a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/TableTest.kt b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/TableTest.kt
index 4aac01a..889d4c9 100644
--- a/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/TableTest.kt
+++ b/ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/TableTest.kt
@@ -23,6 +23,8 @@
 import androidx.ui.core.PxSize
 import androidx.ui.core.Ref
 import androidx.ui.core.ipx
+import androidx.ui.core.max
+import androidx.ui.core.min
 import androidx.ui.core.withDensity
 import androidx.ui.layout.Align
 import androidx.ui.layout.Alignment
@@ -31,6 +33,7 @@
 import androidx.ui.layout.DpConstraints
 import androidx.ui.layout.Table
 import androidx.ui.layout.TableColumnWidth
+import androidx.ui.layout.sum
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -49,8 +52,8 @@
 
         val size = 64.ipx
         val sizeDp = size.toDp()
-        val maxWidth = 256.ipx
-        val maxWidthDp = maxWidth.toDp()
+        val tableWidth = 256.ipx
+        val tableWidthDp = tableWidth.toDp()
 
         val tableSize = Ref<PxSize>()
         val childSize = Array(rows) { Array(columns) { Ref<PxSize>() } }
@@ -59,7 +62,7 @@
 
         show {
             Align(Alignment.TopLeft) {
-                ConstrainedBox(constraints = DpConstraints(maxWidth = maxWidthDp)) {
+                ConstrainedBox(constraints = DpConstraints(maxWidth = tableWidthDp)) {
                     OnChildPositioned(onPositioned = { coordinates ->
                         tableSize.value = coordinates.size
                         positionedLatch.countDown()
@@ -87,17 +90,17 @@
         positionedLatch.await(1, TimeUnit.SECONDS)
 
         assertEquals(
-            PxSize(maxWidth, size * rows),
+            PxSize(tableWidth, size * rows),
             tableSize.value
         )
         for (i in 0 until rows) {
             for (j in 0 until columns) {
                 assertEquals(
-                    PxSize(maxWidth / columns, size),
+                    PxSize(tableWidth / columns, size),
                     childSize[i][j].value
                 )
                 assertEquals(
-                    PxPosition(maxWidth * j / columns, size * i),
+                    PxPosition(tableWidth * j / columns, size * i),
                     childPosition[i][j].value
                 )
             }
@@ -105,7 +108,7 @@
     }
 
     @Test
-    fun testTable_rowHeights() = withDensity(density) {
+    fun testTable_withDifferentRowHeights() = withDensity(density) {
         val rows = 8
         val columns = 8
 
@@ -113,8 +116,8 @@
         val sizeDp = size.toDp()
         val halfSize = 32.ipx
         val halfSizeDp = halfSize.toDp()
-        val maxWidth = 256.ipx
-        val maxWidthDp = maxWidth.toDp()
+        val tableWidth = 256.ipx
+        val tableWidthDp = tableWidth.toDp()
 
         val tableSize = Ref<PxSize>()
         val childSize = Array(rows) { Array(columns) { Ref<PxSize>() } }
@@ -123,7 +126,7 @@
 
         show {
             Align(Alignment.TopLeft) {
-                ConstrainedBox(constraints = DpConstraints(maxWidth = maxWidthDp)) {
+                ConstrainedBox(constraints = DpConstraints(maxWidth = tableWidthDp)) {
                     OnChildPositioned(onPositioned = { coordinates ->
                         tableSize.value = coordinates.size
                         positionedLatch.countDown()
@@ -133,7 +136,7 @@
                                 tableRow {
                                     for (j in 0 until columns) {
                                         Container(
-                                            height = if (j == 0) sizeDp else halfSizeDp,
+                                            height = if (j % 2 == 0) sizeDp else halfSizeDp,
                                             expanded = true
                                         ) {
                                             SaveLayoutInfo(
@@ -154,17 +157,17 @@
         positionedLatch.await(1, TimeUnit.SECONDS)
 
         assertEquals(
-            PxSize(maxWidth, size * rows),
+            PxSize(tableWidth, size * rows),
             tableSize.value
         )
         for (i in 0 until rows) {
             for (j in 0 until columns) {
                 assertEquals(
-                    PxSize(maxWidth / columns, if (j == 0) size else halfSize),
+                    PxSize(tableWidth / columns, if (j % 2 == 0) size else halfSize),
                     childSize[i][j].value
                 )
                 assertEquals(
-                    PxPosition(maxWidth * j / columns, size * i),
+                    PxPosition(tableWidth * j / columns, size * i),
                     childPosition[i][j].value
                 )
             }
@@ -172,7 +175,74 @@
     }
 
     @Test
-    fun testTable_withColumnWidths_wrap() = withDensity(density) {
+    fun testTable_withColumnWidth_flexible() = withDensity(density) {
+        val rows = 8
+        val columns = 8
+
+        val size = 64.ipx
+        val sizeDp = size.toDp()
+        val tableWidth = 256.ipx
+        val tableWidthDp = tableWidth.toDp()
+
+        val tableSize = Ref<PxSize>()
+        val childSize = Array(rows) { Array(columns) { Ref<PxSize>() } }
+        val childPosition = Array(rows) { Array(columns) { Ref<PxPosition>() } }
+        val positionedLatch = CountDownLatch(rows * columns + 1)
+
+        val flexes = Array(columns) { j -> 2f.pow(max(j - 1, 0)) }
+        val totalFlex = flexes.sum()
+
+        show {
+            Align(Alignment.TopLeft) {
+                ConstrainedBox(constraints = DpConstraints(maxWidth = tableWidthDp)) {
+                    OnChildPositioned(onPositioned = { coordinates ->
+                        tableSize.value = coordinates.size
+                        positionedLatch.countDown()
+                    }) {
+                        Table(columnWidth = { j ->
+                            TableColumnWidth.Flexible(flex = flexes[j])
+                        }) {
+                            for (i in 0 until rows) {
+                                tableRow {
+                                    for (j in 0 until columns) {
+                                        Container(height = sizeDp, expanded = true) {
+                                            SaveLayoutInfo(
+                                                size = childSize[i][j],
+                                                position = childPosition[i][j],
+                                                positionedLatch = positionedLatch
+                                            )
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        positionedLatch.await(1, TimeUnit.SECONDS)
+
+        assertEquals(
+            PxSize(tableWidth, size * rows),
+            tableSize.value
+        )
+        for (i in 0 until rows) {
+            for (j in 0 until columns) {
+                assertEquals(
+                    PxSize(tableWidth * flexes[j] / totalFlex, size),
+                    childSize[i][j].value
+                )
+                assertEquals(
+                    PxPosition(tableWidth * flexes.take(j).sum() / totalFlex, size * i),
+                    childPosition[i][j].value
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testTable_withColumnWidth_inflexible_wrap() = withDensity(density) {
         val rows = 8
         val columns = 8
 
@@ -190,7 +260,7 @@
                     tableSize.value = coordinates.size
                     positionedLatch.countDown()
                 }) {
-                    Table(columnWidth = { TableColumnWidth.Wrap }) {
+                    Table(columnWidth = { TableColumnWidth.Inflexible.Wrap }) {
                         for (i in 0 until rows) {
                             tableRow {
                                 for (j in 0 until columns) {
@@ -230,74 +300,7 @@
     }
 
     @Test
-    fun testTable_withColumnWidths_flex() = withDensity(density) {
-        val rows = 8
-        val columns = 8
-
-        val size = 64.ipx
-        val sizeDp = size.toDp()
-        val maxWidth = 256.ipx
-        val maxWidthDp = maxWidth.toDp()
-
-        val tableSize = Ref<PxSize>()
-        val childSize = Array(rows) { Array(columns) { Ref<PxSize>() } }
-        val childPosition = Array(rows) { Array(columns) { Ref<PxPosition>() } }
-        val positionedLatch = CountDownLatch(rows * columns + 1)
-
-        val flexes = Array(columns) { j -> 2f.pow(max(j - 1, 0)) }
-        val totalFlex = flexes.sum()
-
-        show {
-            Align(Alignment.TopLeft) {
-                ConstrainedBox(constraints = DpConstraints(maxWidth = maxWidthDp)) {
-                    OnChildPositioned(onPositioned = { coordinates ->
-                        tableSize.value = coordinates.size
-                        positionedLatch.countDown()
-                    }) {
-                        Table(columnWidth = { j ->
-                            TableColumnWidth.Flex(flex = flexes[j])
-                        }) {
-                            for (i in 0 until rows) {
-                                tableRow {
-                                    for (j in 0 until columns) {
-                                        Container(height = sizeDp, expanded = true) {
-                                            SaveLayoutInfo(
-                                                size = childSize[i][j],
-                                                position = childPosition[i][j],
-                                                positionedLatch = positionedLatch
-                                            )
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        positionedLatch.await(1, TimeUnit.SECONDS)
-
-        assertEquals(
-            PxSize(maxWidth, size * rows),
-            tableSize.value
-        )
-        for (i in 0 until rows) {
-            for (j in 0 until columns) {
-                assertEquals(
-                    PxSize(maxWidth * flexes[j] / totalFlex, size),
-                    childSize[i][j].value
-                )
-                assertEquals(
-                    PxPosition(maxWidth * flexes.take(j).sum() / totalFlex, size * i),
-                    childPosition[i][j].value
-                )
-            }
-        }
-    }
-
-    @Test
-    fun testTable_withColumnWidths_fixed() = withDensity(density) {
+    fun testTable_withColumnWidth_inflexible_fixed() = withDensity(density) {
         val rows = 8
         val columns = 8
 
@@ -315,7 +318,7 @@
                     tableSize.value = coordinates.size
                     positionedLatch.countDown()
                 }) {
-                    Table(columnWidth = { TableColumnWidth.Fixed(width = sizeDp) }) {
+                    Table(columnWidth = { TableColumnWidth.Inflexible.Fixed(width = sizeDp) }) {
                         for (i in 0 until rows) {
                             tableRow {
                                 for (j in 0 until columns) {
@@ -355,14 +358,14 @@
     }
 
     @Test
-    fun testTable_withColumnWidths_fraction() = withDensity(density) {
+    fun testTable_withColumnWidth_inflexible_fraction() = withDensity(density) {
         val rows = 8
         val columns = 8
 
         val size = 64.ipx
         val sizeDp = size.toDp()
-        val maxWidth = 256.ipx
-        val maxWidthDp = maxWidth.toDp()
+        val tableWidth = 256.ipx
+        val tableWidthDp = tableWidth.toDp()
 
         val tableSize = Ref<PxSize>()
         val childSize = Array(rows) { Array(columns) { Ref<PxSize>() } }
@@ -373,13 +376,13 @@
 
         show {
             Align(Alignment.TopLeft) {
-                ConstrainedBox(constraints = DpConstraints(maxWidth = maxWidthDp)) {
+                ConstrainedBox(constraints = DpConstraints(maxWidth = tableWidthDp)) {
                     OnChildPositioned(onPositioned = { coordinates ->
                         tableSize.value = coordinates.size
                         positionedLatch.countDown()
                     }) {
                         Table(columnWidth = { j ->
-                            TableColumnWidth.Fraction(fraction = fractions[j])
+                            TableColumnWidth.Inflexible.Fraction(fraction = fractions[j])
                         }) {
                             for (i in 0 until rows) {
                                 tableRow {
@@ -403,17 +406,17 @@
         positionedLatch.await(1, TimeUnit.SECONDS)
 
         assertEquals(
-            PxSize(maxWidth * fractions.sum(), size * rows),
+            PxSize(tableWidth * fractions.sum(), size * rows),
             tableSize.value
         )
         for (i in 0 until rows) {
             for (j in 0 until columns) {
                 assertEquals(
-                    PxSize(maxWidth * fractions[j], size),
+                    PxSize(tableWidth * fractions[j], size),
                     childSize[i][j].value
                 )
                 assertEquals(
-                    PxPosition(maxWidth * fractions.take(j).sum(), size * i),
+                    PxPosition(tableWidth * fractions.take(j).sum(), size * i),
                     childPosition[i][j].value
                 )
             }
@@ -421,16 +424,16 @@
     }
 
     @Test
-    fun testTable_withColumnWidths_mixed() = withDensity(density) {
+    fun testTable_withColumnWidth_inflexible_min() = withDensity(density) {
         val rows = 8
-        val columns = 5
+        val columns = 8
 
         val size = 64.ipx
         val sizeDp = size.toDp()
-        val halfSize = 32.ipx
-        val halfSizeDp = halfSize.toDp()
-        val maxWidth = 256.ipx
-        val maxWidthDp = maxWidth.toDp()
+        val minWidth = 24.ipx
+        val minWidthDp = minWidth.toDp()
+        val tableWidth = 256.ipx
+        val tableWidthDp = tableWidth.toDp()
 
         val tableSize = Ref<PxSize>()
         val childSize = Array(rows) { Array(columns) { Ref<PxSize>() } }
@@ -439,18 +442,416 @@
 
         show {
             Align(Alignment.TopLeft) {
-                ConstrainedBox(constraints = DpConstraints(maxWidth = maxWidthDp)) {
+                ConstrainedBox(constraints = DpConstraints(maxWidth = tableWidthDp)) {
+                    OnChildPositioned(onPositioned = { coordinates ->
+                        tableSize.value = coordinates.size
+                        positionedLatch.countDown()
+                    }) {
+                        Table(columnWidth = { j ->
+                            TableColumnWidth.Inflexible.Min(
+                                a = TableColumnWidth.Inflexible.Fixed(width = minWidthDp),
+                                b = TableColumnWidth.Inflexible.Fraction(
+                                    fraction = if (j % 2 == 0) 1f / columns else 1f / (columns * 2)
+                                )
+                            )
+                        }) {
+                            for (i in 0 until rows) {
+                                tableRow {
+                                    for (j in 0 until columns) {
+                                        Container(height = sizeDp, expanded = true) {
+                                            SaveLayoutInfo(
+                                                size = childSize[i][j],
+                                                position = childPosition[i][j],
+                                                positionedLatch = positionedLatch
+                                            )
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        positionedLatch.await(1, TimeUnit.SECONDS)
+
+        val expectedWidths = Array(columns) { j ->
+            min(minWidth, if (j % 2 == 0) tableWidth / columns else tableWidth / (columns * 2))
+        }
+
+        assertEquals(
+            PxSize(expectedWidths.sum(), size * rows),
+            tableSize.value
+        )
+        for (i in 0 until rows) {
+            for (j in 0 until columns) {
+                assertEquals(
+                    PxSize(expectedWidths[j], size),
+                    childSize[i][j].value
+                )
+                assertEquals(
+                    PxPosition(expectedWidths.take(j).sum(), size * i),
+                    childPosition[i][j].value
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testTable_withColumnWidth_inflexible_max() = withDensity(density) {
+        val rows = 8
+        val columns = 8
+
+        val size = 64.ipx
+        val sizeDp = size.toDp()
+        val maxWidth = 24.ipx
+        val maxWidthDp = maxWidth.toDp()
+        val tableWidth = 256.ipx
+        val tableWidthDp = tableWidth.toDp()
+
+        val tableSize = Ref<PxSize>()
+        val childSize = Array(rows) { Array(columns) { Ref<PxSize>() } }
+        val childPosition = Array(rows) { Array(columns) { Ref<PxPosition>() } }
+        val positionedLatch = CountDownLatch(rows * columns + 1)
+
+        show {
+            Align(Alignment.TopLeft) {
+                ConstrainedBox(constraints = DpConstraints(maxWidth = tableWidthDp)) {
+                    OnChildPositioned(onPositioned = { coordinates ->
+                        tableSize.value = coordinates.size
+                        positionedLatch.countDown()
+                    }) {
+                        Table(columnWidth = { j ->
+                            TableColumnWidth.Inflexible.Max(
+                                a = TableColumnWidth.Inflexible.Fixed(width = maxWidthDp),
+                                b = TableColumnWidth.Inflexible.Fraction(
+                                    fraction = if (j % 2 == 0) 1f / columns else 1f / (columns * 2)
+                                )
+                            )
+                        }) {
+                            for (i in 0 until rows) {
+                                tableRow {
+                                    for (j in 0 until columns) {
+                                        Container(height = sizeDp, expanded = true) {
+                                            SaveLayoutInfo(
+                                                size = childSize[i][j],
+                                                position = childPosition[i][j],
+                                                positionedLatch = positionedLatch
+                                            )
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        positionedLatch.await(1, TimeUnit.SECONDS)
+
+        val expectedWidths = Array(columns) { j ->
+            max(maxWidth, if (j % 2 == 0) tableWidth / columns else tableWidth / (columns * 2))
+        }
+
+        assertEquals(
+            PxSize(expectedWidths.sum(), size * rows),
+            tableSize.value
+        )
+        for (i in 0 until rows) {
+            for (j in 0 until columns) {
+                assertEquals(
+                    PxSize(expectedWidths[j], size),
+                    childSize[i][j].value
+                )
+                assertEquals(
+                    PxPosition(expectedWidths.take(j).sum(), size * i),
+                    childPosition[i][j].value
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testTable_withColumnWidth_inflexible_min_oneWrap() = withDensity(density) {
+        val rows = 8
+        val columns = 8
+
+        val size = 64.ipx
+        val sizeDp = size.toDp()
+        val halfSize = 32.ipx
+        val halfSizeDp = halfSize.toDp()
+
+        val tableSize = Ref<PxSize>()
+        val childSize = Array(rows) { Array(columns) { Ref<PxSize>() } }
+        val childPosition = Array(rows) { Array(columns) { Ref<PxPosition>() } }
+        val positionedLatch = CountDownLatch(rows * columns + 1)
+
+        show {
+            Align(Alignment.TopLeft) {
+                OnChildPositioned(onPositioned = { coordinates ->
+                    tableSize.value = coordinates.size
+                    positionedLatch.countDown()
+                }) {
+                    Table(columnWidth = { TableColumnWidth.Inflexible.Min(
+                        a = TableColumnWidth.Inflexible.Wrap,
+                        b = TableColumnWidth.Inflexible.Fixed(width = sizeDp)
+                    ) }) {
+                        for (i in 0 until rows) {
+                            tableRow {
+                                for (j in 0 until columns) {
+                                    Container(width = halfSizeDp, height = sizeDp) {
+                                        SaveLayoutInfo(
+                                            size = childSize[i][j],
+                                            position = childPosition[i][j],
+                                            positionedLatch = positionedLatch
+                                        )
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        positionedLatch.await(1, TimeUnit.SECONDS)
+
+        assertEquals(
+            PxSize(halfSize * columns, size * rows),
+            tableSize.value
+        )
+        for (i in 0 until rows) {
+            for (j in 0 until columns) {
+                assertEquals(
+                    PxSize(halfSize, size),
+                    childSize[i][j].value
+                )
+                assertEquals(
+                    PxPosition(halfSize * j, size * i),
+                    childPosition[i][j].value
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testTable_withColumnWidth_inflexible_max_oneWrap() = withDensity(density) {
+        val rows = 8
+        val columns = 8
+
+        val size = 64.ipx
+        val sizeDp = size.toDp()
+        val halfSize = 32.ipx
+        val halfSizeDp = halfSize.toDp()
+
+        val tableSize = Ref<PxSize>()
+        val childSize = Array(rows) { Array(columns) { Ref<PxSize>() } }
+        val childPosition = Array(rows) { Array(columns) { Ref<PxPosition>() } }
+        val positionedLatch = CountDownLatch(rows * columns + 1)
+
+        show {
+            Align(Alignment.TopLeft) {
+                OnChildPositioned(onPositioned = { coordinates ->
+                    tableSize.value = coordinates.size
+                    positionedLatch.countDown()
+                }) {
+                    Table(columnWidth = { TableColumnWidth.Inflexible.Max(
+                        a = TableColumnWidth.Inflexible.Wrap,
+                        b = TableColumnWidth.Inflexible.Fixed(width = sizeDp)
+                    ) }) {
+                        for (i in 0 until rows) {
+                            tableRow {
+                                for (j in 0 until columns) {
+                                    Container(width = halfSizeDp, height = sizeDp) {
+                                        SaveLayoutInfo(
+                                            size = childSize[i][j],
+                                            position = childPosition[i][j],
+                                            positionedLatch = positionedLatch
+                                        )
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        positionedLatch.await(1, TimeUnit.SECONDS)
+
+        assertEquals(
+            PxSize(size * columns, size * rows),
+            tableSize.value
+        )
+        for (i in 0 until rows) {
+            for (j in 0 until columns) {
+                assertEquals(
+                    PxSize(halfSize, size),
+                    childSize[i][j].value
+                )
+                assertEquals(
+                    PxPosition(size * j, size * i),
+                    childPosition[i][j].value
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testTable_withColumnWidth_inflexible_min_bothWrap() = withDensity(density) {
+        val rows = 8
+        val columns = 8
+
+        val size = 64.ipx
+        val sizeDp = size.toDp()
+
+        val tableSize = Ref<PxSize>()
+        val childSize = Array(rows) { Array(columns) { Ref<PxSize>() } }
+        val childPosition = Array(rows) { Array(columns) { Ref<PxPosition>() } }
+        val positionedLatch = CountDownLatch(rows * columns + 1)
+
+        show {
+            Align(Alignment.TopLeft) {
+                OnChildPositioned(onPositioned = { coordinates ->
+                    tableSize.value = coordinates.size
+                    positionedLatch.countDown()
+                }) {
+                    Table(columnWidth = { TableColumnWidth.Inflexible.Min(
+                        a = TableColumnWidth.Inflexible.Wrap,
+                        b = TableColumnWidth.Inflexible.Wrap
+                    ) }) {
+                        for (i in 0 until rows) {
+                            tableRow {
+                                for (j in 0 until columns) {
+                                    Container(width = sizeDp, height = sizeDp) {
+                                        SaveLayoutInfo(
+                                            size = childSize[i][j],
+                                            position = childPosition[i][j],
+                                            positionedLatch = positionedLatch
+                                        )
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        positionedLatch.await(1, TimeUnit.SECONDS)
+
+        assertEquals(
+            PxSize(size * columns, size * rows),
+            tableSize.value
+        )
+        for (i in 0 until rows) {
+            for (j in 0 until columns) {
+                assertEquals(
+                    PxSize(size, size),
+                    childSize[i][j].value
+                )
+                assertEquals(
+                    PxPosition(size * j, size * i),
+                    childPosition[i][j].value
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testTable_withColumnWidth_inflexible_max_bothWrap() = withDensity(density) {
+        val rows = 8
+        val columns = 8
+
+        val size = 64.ipx
+        val sizeDp = size.toDp()
+
+        val tableSize = Ref<PxSize>()
+        val childSize = Array(rows) { Array(columns) { Ref<PxSize>() } }
+        val childPosition = Array(rows) { Array(columns) { Ref<PxPosition>() } }
+        val positionedLatch = CountDownLatch(rows * columns + 1)
+
+        show {
+            Align(Alignment.TopLeft) {
+                OnChildPositioned(onPositioned = { coordinates ->
+                    tableSize.value = coordinates.size
+                    positionedLatch.countDown()
+                }) {
+                    Table(columnWidth = { TableColumnWidth.Inflexible.Max(
+                        a = TableColumnWidth.Inflexible.Wrap,
+                        b = TableColumnWidth.Inflexible.Wrap
+                    ) }) {
+                        for (i in 0 until rows) {
+                            tableRow {
+                                for (j in 0 until columns) {
+                                    Container(width = sizeDp, height = sizeDp) {
+                                        SaveLayoutInfo(
+                                            size = childSize[i][j],
+                                            position = childPosition[i][j],
+                                            positionedLatch = positionedLatch
+                                        )
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        positionedLatch.await(1, TimeUnit.SECONDS)
+
+        assertEquals(
+            PxSize(size * columns, size * rows),
+            tableSize.value
+        )
+        for (i in 0 until rows) {
+            for (j in 0 until columns) {
+                assertEquals(
+                    PxSize(size, size),
+                    childSize[i][j].value
+                )
+                assertEquals(
+                    PxPosition(size * j, size * i),
+                    childPosition[i][j].value
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testTable_withDifferentColumnWidths() = withDensity(density) {
+        val rows = 8
+        val columns = 5
+
+        val size = 64.ipx
+        val sizeDp = size.toDp()
+        val halfSize = 32.ipx
+        val halfSizeDp = halfSize.toDp()
+        val tableWidth = 256.ipx
+        val tableWidthDp = tableWidth.toDp()
+
+        val tableSize = Ref<PxSize>()
+        val childSize = Array(rows) { Array(columns) { Ref<PxSize>() } }
+        val childPosition = Array(rows) { Array(columns) { Ref<PxPosition>() } }
+        val positionedLatch = CountDownLatch(rows * columns + 1)
+
+        show {
+            Align(Alignment.TopLeft) {
+                ConstrainedBox(constraints = DpConstraints(maxWidth = tableWidthDp)) {
                     OnChildPositioned(onPositioned = { coordinates ->
                         tableSize.value = coordinates.size
                         positionedLatch.countDown()
                     }) {
                         Table(columnWidth = { j ->
                             when (j) {
-                                0 -> TableColumnWidth.Wrap
-                                1 -> TableColumnWidth.Flex(flex = 1f)
-                                2 -> TableColumnWidth.Flex(flex = 3f)
-                                3 -> TableColumnWidth.Fixed(width = sizeDp)
-                                else -> TableColumnWidth.Fraction(fraction = 0.5f)
+                                0 -> TableColumnWidth.Inflexible.Wrap
+                                1 -> TableColumnWidth.Flexible(flex = 1f)
+                                2 -> TableColumnWidth.Flexible(flex = 3f)
+                                3 -> TableColumnWidth.Inflexible.Fixed(width = sizeDp)
+                                else -> TableColumnWidth.Inflexible.Fraction(fraction = 0.5f)
                             }
                         }) {
                             for (i in 0 until rows) {
@@ -501,10 +902,9 @@
         positionedLatch.await(1, TimeUnit.SECONDS)
 
         assertEquals(
-            PxSize(maxWidth, size * rows),
+            PxSize(tableWidth, size * rows),
             tableSize.value
         )
-
         for (i in 0 until rows) {
             // Wrap column 0
             assertEquals(
@@ -517,7 +917,7 @@
             )
             // Flex column 1
             assertEquals(
-                PxSize((maxWidth / 2 - size - halfSize) / 4, size),
+                PxSize((tableWidth / 2 - size - halfSize) / 4, size),
                 childSize[i][1].value
             )
             assertEquals(
@@ -526,11 +926,11 @@
             )
             // Flex column 2
             assertEquals(
-                PxSize((maxWidth / 2 - size - halfSize) * 3 / 4, size),
+                PxSize((tableWidth / 2 - size - halfSize) * 3 / 4, size),
                 childSize[i][2].value
             )
             assertEquals(
-                PxPosition(halfSize + (maxWidth / 2 - size - halfSize) / 4, size * i),
+                PxPosition(halfSize + (tableWidth / 2 - size - halfSize) / 4, size * i),
                 childPosition[i][2].value
             )
             // Fixed column 3
@@ -539,16 +939,16 @@
                 childSize[i][3].value
             )
             assertEquals(
-                PxPosition(maxWidth / 2 - size, size * i),
+                PxPosition(tableWidth / 2 - size, size * i),
                 childPosition[i][3].value
             )
             // Fraction column 4
             assertEquals(
-                PxSize(maxWidth / 2, size),
+                PxSize(tableWidth / 2, size),
                 childSize[i][4].value
             )
             assertEquals(
-                PxPosition(maxWidth / 2, size * i),
+                PxPosition(tableWidth / 2, size * i),
                 childPosition[i][4].value
             )
         }
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Table.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Table.kt
index 3965a5a..1dd6894 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Table.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Table.kt
@@ -32,6 +32,7 @@
 import androidx.ui.core.coerceIn
 import androidx.ui.core.isFinite
 import androidx.ui.core.max
+import androidx.ui.core.min
 
 /**
  * Collects information about the children of a [Table] when
@@ -61,27 +62,39 @@
  */
 sealed class TableColumnWidth {
     /**
-     * Sizes the column to be the width of the widest child in that column.
+     * Sizes the column by taking a part of the remaining space according
+     * to [flex] once all the inflexible columns have been measured.
      */
-    object Wrap : TableColumnWidth()
+    data class Flexible(internal val flex: Float) : TableColumnWidth()
 
-    /**
-     * Sizes the column by taking a part of the remaining space
-     * once all the other columns have been measured according to [flex].
-     */
-    data class Flex(internal val flex: Float) : TableColumnWidth()
+    sealed class Inflexible : TableColumnWidth() {
+        /**
+         * Sizes the column to be the width of the widest child in that column.
+         */
+        object Wrap : Inflexible()
 
-    /**
-     * Sizes the column to a specific width.
-     */
-    data class Fixed(internal val width: Dp) : TableColumnWidth()
+        /**
+         * Sizes the column to a specific width.
+         */
+        data class Fixed(internal val width: Dp) : Inflexible()
 
-    /**
-     * Sizes the column to a fraction of the table’s maximum width constraint.
-     */
-    data class Fraction(
-        @FloatRange(from = 0.0, to = 1.0) internal val fraction: Float
-    ) : TableColumnWidth()
+        /**
+         * Sizes the column to a fraction of the table’s maximum width constraint.
+         */
+        data class Fraction(
+            @FloatRange(from = 0.0, to = 1.0) internal val fraction: Float
+        ) : Inflexible()
+
+        /**
+         * Sizes the column such that it is the size that is the min of two width specifications.
+         */
+        data class Min(internal val a: Inflexible, internal val b: Inflexible) : Inflexible()
+
+        /**
+         * Sizes the column such that it is the size that is the max of two width specifications.
+         */
+        data class Max(internal val a: Inflexible, internal val b: Inflexible) : Inflexible()
+    }
 }
 
 /**
@@ -96,7 +109,7 @@
 @Composable
 fun Table(
     childAlignment: Alignment = Alignment.TopLeft,
-    columnWidth: (columnIndex: Int) -> TableColumnWidth = { TableColumnWidth.Flex(1f) },
+    columnWidth: (columnIndex: Int) -> TableColumnWidth = { TableColumnWidth.Flexible(1f) },
     @Children(composable = false) block: TableChildren.() -> Unit
 ) {
     val children: @Composable() () -> Unit = with(TableChildren()) {
@@ -111,8 +124,8 @@
         // Group the measurables into rows using rowGroup.
         val measurables = m.groupBy { it.rowGroup }.values.toTypedArray()
 
-        val rows = measurables.size
-        val columns = measurables.map { it.size }.max() ?: 0
+        val rowCount = measurables.size
+        val columnCount = measurables.map { it.size }.max() ?: 0
 
         var totalFlex = 0f
         var availableSpace = if (constraints.maxWidth.isFinite()) {
@@ -121,60 +134,80 @@
             constraints.minWidth
         }
 
-        val rowHeights = Array(rows) { IntPx.Zero }
-        val columnWidths = Array(columns) { IntPx.Zero }
+        val rowHeights = Array(rowCount) { IntPx.Zero }
+        val columnWidths = Array(columnCount) { IntPx.Zero }
 
-        val placeables = Array(rows) { arrayOfNulls<Placeable>(columns) }
+        val placeables = Array(rowCount) { arrayOfNulls<Placeable>(columnCount) }
 
-        // Compute widths of non-flex columns.
-        for (j in 0 until columns) {
-            when (val spec = columnWidth(j)) {
-                is TableColumnWidth.Flex -> {
-                    totalFlex += spec.flex
+        // Compute the actual width of a column for the given specification.
+        fun TableColumnWidth.Inflexible.computeWidth(column: Int): IntPx {
+            return when (this) {
+                is TableColumnWidth.Inflexible.Wrap -> {
+                    // Measure children in this column to get their preferred widths.
+                    // TODO(calintat): Use minIntrinsicWidth and delay measuring until later.
+                    var result = IntPx.Zero
+                    for (row in 0 until rowCount) {
+                        val p = placeables[row][column]
+                        if (p != null) {
+                            result = max(result, p.width)
+                        } else {
+                            val placeable = measurables[row][column].measure(Constraints())
+                            placeables[row][column] = placeable
+                            result = max(result, placeable.width)
+                        }
+                    }
+                    result
                 }
-                is TableColumnWidth.Fixed -> {
-                    columnWidths[j] = spec.width.toIntPx()
+                is TableColumnWidth.Inflexible.Fixed -> {
+                    this.width.toIntPx()
                 }
-                is TableColumnWidth.Fraction -> {
-                    columnWidths[j] = if (constraints.maxWidth.isFinite()) {
-                        constraints.maxWidth * spec.fraction
+                is TableColumnWidth.Inflexible.Fraction -> {
+                    if (constraints.maxWidth.isFinite()) {
+                        constraints.maxWidth * this.fraction
                     } else {
                         IntPx.Zero
                     }
                 }
-                is TableColumnWidth.Wrap -> {
-                    // Measure children in intrinsic columns.
-                    for (i in 0 until rows) {
-                        val placeable = measurables[i][j].measure(Constraints())
-                        placeables[i][j] = placeable
-                        rowHeights[i] = max(rowHeights[i], placeable.height)
-                        columnWidths[j] = max(columnWidths[j], placeable.width)
-                    }
+                is TableColumnWidth.Inflexible.Min -> {
+                    min(this.a.computeWidth(column), this.b.computeWidth(column))
+                }
+                is TableColumnWidth.Inflexible.Max -> {
+                    max(this.a.computeWidth(column), this.b.computeWidth(column))
                 }
             }
-            availableSpace -= columnWidths[j]
+        }
+
+        // Compute widths of inflexible columns.
+        for (column in 0 until columnCount) {
+            when (val spec = columnWidth(column)) {
+                is TableColumnWidth.Flexible -> {
+                    totalFlex += spec.flex
+                }
+                is TableColumnWidth.Inflexible -> {
+                    columnWidths[column] = spec.computeWidth(column)
+                    availableSpace -= columnWidths[column]
+                }
+            }
         }
 
         availableSpace = availableSpace.coerceAtLeast(IntPx.Zero)
 
         // Compute widths of flex columns.
-        for (j in 0 until columns) {
-            val spec = columnWidth(j)
-            if (spec is TableColumnWidth.Flex) {
-                columnWidths[j] = availableSpace * (spec.flex / totalFlex)
+        for (column in 0 until columnCount) {
+            val spec = columnWidth(column)
+            if (spec is TableColumnWidth.Flexible) {
+                columnWidths[column] = availableSpace * (spec.flex / totalFlex)
             }
         }
 
-        // Measure the remaining children.
-        for (i in 0 until rows) {
-            for (j in 0 until columns) {
-                if (placeables[i][j] == null) {
-                    val placeable = measurables[i][j].measure(
-                        Constraints(maxWidth = columnWidths[j])
-                    )
-                    placeables[i][j] = placeable
-                    rowHeights[i] = max(rowHeights[i], placeable.height)
+        // Measure the remaining children and calculate row heights.
+        for (row in 0 until rowCount) {
+            for (column in 0 until columnCount) {
+                if (placeables[row][column] == null) {
+                    placeables[row][column] = measurables[row][column].measure(
+                        Constraints(minWidth = IntPx.Zero, maxWidth = columnWidths[column]))
                 }
+                rowHeights[row] = max(rowHeights[row], placeables[row][column]!!.height)
             }
         }
 
@@ -183,18 +216,18 @@
         val tableHeight = rowHeights.sum().coerceIn(constraints.minHeight, constraints.maxHeight)
 
         layout(tableWidth, tableHeight) {
-            for (i in 0 until rows) {
-                for (j in 0 until columns) {
-                    val placeable = placeables[i][j]!!
+            for (row in 0 until rowCount) {
+                for (column in 0 until columnCount) {
+                    val placeable = placeables[row][column]!!
                     val position = childAlignment.align(
                         IntPxSize(
-                            width = columnWidths[j] - placeable.width,
-                            height = rowHeights[i] - placeable.height
+                            width = columnWidths[column] - placeable.width,
+                            height = rowHeights[row] - placeable.height
                         )
                     )
                     placeable.place(
-                        x = columnWidths.take(j).sum() + position.x,
-                        y = rowHeights.take(i).sum() + position.y
+                        x = columnWidths.take(column).sum() + position.x,
+                        y = rowHeights.take(row).sum() + position.y
                     )
                 }
             }
@@ -202,5 +235,5 @@
     }
 }
 
-private fun Array<IntPx>.sum() = this.fold(IntPx.Zero) { a, b -> a + b }
-private fun Collection<IntPx>.sum() = this.fold(IntPx.Zero) { a, b -> a + b }
+internal fun Array<IntPx>.sum() = this.fold(IntPx.Zero) { a, b -> a + b }
+internal fun Collection<IntPx>.sum() = this.fold(IntPx.Zero) { a, b -> a + b }
\ No newline at end of file
diff --git a/ui/ui-material/api/1.0.0-alpha01.txt b/ui/ui-material/api/1.0.0-alpha01.txt
index 17ead5a..eba63b6 100644
--- a/ui/ui-material/api/1.0.0-alpha01.txt
+++ b/ui/ui-material/api/1.0.0-alpha01.txt
@@ -9,7 +9,7 @@
 }), kotlin.jvm.functions.Function0<kotlin.Unit>? navigationIcon = (kotlin.jvm.functions.Function0<? extends kotlin.Unit>)null, kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton = (kotlin.jvm.functions.Function0<? extends kotlin.Unit>)null, androidx.ui.material.BottomAppBar.FabPosition fabPosition = Center, java.util.List<? extends T>? contextualActions = null, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action = {});
     method public static <T> void TopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title = {}, androidx.ui.graphics.Color color = +themeColor({ 
     primary
-}), kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon = {}, java.util.List<? extends T>? contextualActions = null, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action = {});
+}), kotlin.jvm.functions.Function0<kotlin.Unit>? navigationIcon = (kotlin.jvm.functions.Function0<? extends kotlin.Unit>)null, java.util.List<? extends T>? contextualActions = null, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action = {});
   }
 
   public final class BottomAppBar {
@@ -211,7 +211,26 @@
   public final class TabKt {
     ctor public TabKt();
     method public static void Tab(String? text = null, androidx.ui.painting.Image? icon = null, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onSelected);
-    method public static <T> void TabRow(java.util.List<? extends T> items, int selectedIndex, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> tab);
+    method public static <T> void TabRow(java.util.List<? extends T> items, int selectedIndex, kotlin.jvm.functions.Function1<? super java.util.List<androidx.ui.material.TabRow.TabPosition>,kotlin.Unit> indicatorContainer = { tabPositions -> TabRow.IndicatorContainer(tabPositions, selectedIndex, { 
+    TabRow.Indicator()
+}) }, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> tab);
+  }
+
+  public final class TabRow {
+    method public void Indicator();
+    method public void IndicatorContainer(java.util.List<androidx.ui.material.TabRow.TabPosition> tabPositions, int selectedIndex, kotlin.jvm.functions.Function0<kotlin.Unit> indicator);
+    field public static final androidx.ui.material.TabRow! INSTANCE;
+  }
+
+  public static final class TabRow.TabPosition {
+    ctor public TabRow.TabPosition(androidx.ui.core.Dp xOffset, androidx.ui.core.Dp width, androidx.ui.core.Dp height);
+    method public androidx.ui.core.Dp component1();
+    method public androidx.ui.core.Dp component2();
+    method public androidx.ui.core.Dp component3();
+    method public androidx.ui.material.TabRow.TabPosition copy(androidx.ui.core.Dp xOffset, androidx.ui.core.Dp width, androidx.ui.core.Dp height);
+    method public androidx.ui.core.Dp getHeight();
+    method public androidx.ui.core.Dp getWidth();
+    method public androidx.ui.core.Dp getXOffset();
   }
 
   public final class TextKt {
diff --git a/ui/ui-material/api/current.txt b/ui/ui-material/api/current.txt
index 17ead5a..eba63b6 100644
--- a/ui/ui-material/api/current.txt
+++ b/ui/ui-material/api/current.txt
@@ -9,7 +9,7 @@
 }), kotlin.jvm.functions.Function0<kotlin.Unit>? navigationIcon = (kotlin.jvm.functions.Function0<? extends kotlin.Unit>)null, kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton = (kotlin.jvm.functions.Function0<? extends kotlin.Unit>)null, androidx.ui.material.BottomAppBar.FabPosition fabPosition = Center, java.util.List<? extends T>? contextualActions = null, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action = {});
     method public static <T> void TopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title = {}, androidx.ui.graphics.Color color = +themeColor({ 
     primary
-}), kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon = {}, java.util.List<? extends T>? contextualActions = null, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action = {});
+}), kotlin.jvm.functions.Function0<kotlin.Unit>? navigationIcon = (kotlin.jvm.functions.Function0<? extends kotlin.Unit>)null, java.util.List<? extends T>? contextualActions = null, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action = {});
   }
 
   public final class BottomAppBar {
@@ -211,7 +211,26 @@
   public final class TabKt {
     ctor public TabKt();
     method public static void Tab(String? text = null, androidx.ui.painting.Image? icon = null, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onSelected);
-    method public static <T> void TabRow(java.util.List<? extends T> items, int selectedIndex, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> tab);
+    method public static <T> void TabRow(java.util.List<? extends T> items, int selectedIndex, kotlin.jvm.functions.Function1<? super java.util.List<androidx.ui.material.TabRow.TabPosition>,kotlin.Unit> indicatorContainer = { tabPositions -> TabRow.IndicatorContainer(tabPositions, selectedIndex, { 
+    TabRow.Indicator()
+}) }, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> tab);
+  }
+
+  public final class TabRow {
+    method public void Indicator();
+    method public void IndicatorContainer(java.util.List<androidx.ui.material.TabRow.TabPosition> tabPositions, int selectedIndex, kotlin.jvm.functions.Function0<kotlin.Unit> indicator);
+    field public static final androidx.ui.material.TabRow! INSTANCE;
+  }
+
+  public static final class TabRow.TabPosition {
+    ctor public TabRow.TabPosition(androidx.ui.core.Dp xOffset, androidx.ui.core.Dp width, androidx.ui.core.Dp height);
+    method public androidx.ui.core.Dp component1();
+    method public androidx.ui.core.Dp component2();
+    method public androidx.ui.core.Dp component3();
+    method public androidx.ui.material.TabRow.TabPosition copy(androidx.ui.core.Dp xOffset, androidx.ui.core.Dp width, androidx.ui.core.Dp height);
+    method public androidx.ui.core.Dp getHeight();
+    method public androidx.ui.core.Dp getWidth();
+    method public androidx.ui.core.Dp getXOffset();
   }
 
   public final class TextKt {
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 17ead5a..eba63b6 100644
--- a/ui/ui-material/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-material/api/restricted_1.0.0-alpha01.txt
@@ -9,7 +9,7 @@
 }), kotlin.jvm.functions.Function0<kotlin.Unit>? navigationIcon = (kotlin.jvm.functions.Function0<? extends kotlin.Unit>)null, kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton = (kotlin.jvm.functions.Function0<? extends kotlin.Unit>)null, androidx.ui.material.BottomAppBar.FabPosition fabPosition = Center, java.util.List<? extends T>? contextualActions = null, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action = {});
     method public static <T> void TopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title = {}, androidx.ui.graphics.Color color = +themeColor({ 
     primary
-}), kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon = {}, java.util.List<? extends T>? contextualActions = null, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action = {});
+}), kotlin.jvm.functions.Function0<kotlin.Unit>? navigationIcon = (kotlin.jvm.functions.Function0<? extends kotlin.Unit>)null, java.util.List<? extends T>? contextualActions = null, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action = {});
   }
 
   public final class BottomAppBar {
@@ -211,7 +211,26 @@
   public final class TabKt {
     ctor public TabKt();
     method public static void Tab(String? text = null, androidx.ui.painting.Image? icon = null, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onSelected);
-    method public static <T> void TabRow(java.util.List<? extends T> items, int selectedIndex, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> tab);
+    method public static <T> void TabRow(java.util.List<? extends T> items, int selectedIndex, kotlin.jvm.functions.Function1<? super java.util.List<androidx.ui.material.TabRow.TabPosition>,kotlin.Unit> indicatorContainer = { tabPositions -> TabRow.IndicatorContainer(tabPositions, selectedIndex, { 
+    TabRow.Indicator()
+}) }, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> tab);
+  }
+
+  public final class TabRow {
+    method public void Indicator();
+    method public void IndicatorContainer(java.util.List<androidx.ui.material.TabRow.TabPosition> tabPositions, int selectedIndex, kotlin.jvm.functions.Function0<kotlin.Unit> indicator);
+    field public static final androidx.ui.material.TabRow! INSTANCE;
+  }
+
+  public static final class TabRow.TabPosition {
+    ctor public TabRow.TabPosition(androidx.ui.core.Dp xOffset, androidx.ui.core.Dp width, androidx.ui.core.Dp height);
+    method public androidx.ui.core.Dp component1();
+    method public androidx.ui.core.Dp component2();
+    method public androidx.ui.core.Dp component3();
+    method public androidx.ui.material.TabRow.TabPosition copy(androidx.ui.core.Dp xOffset, androidx.ui.core.Dp width, androidx.ui.core.Dp height);
+    method public androidx.ui.core.Dp getHeight();
+    method public androidx.ui.core.Dp getWidth();
+    method public androidx.ui.core.Dp getXOffset();
   }
 
   public final class TextKt {
diff --git a/ui/ui-material/api/restricted_current.txt b/ui/ui-material/api/restricted_current.txt
index 17ead5a..eba63b6 100644
--- a/ui/ui-material/api/restricted_current.txt
+++ b/ui/ui-material/api/restricted_current.txt
@@ -9,7 +9,7 @@
 }), kotlin.jvm.functions.Function0<kotlin.Unit>? navigationIcon = (kotlin.jvm.functions.Function0<? extends kotlin.Unit>)null, kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton = (kotlin.jvm.functions.Function0<? extends kotlin.Unit>)null, androidx.ui.material.BottomAppBar.FabPosition fabPosition = Center, java.util.List<? extends T>? contextualActions = null, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action = {});
     method public static <T> void TopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title = {}, androidx.ui.graphics.Color color = +themeColor({ 
     primary
-}), kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon = {}, java.util.List<? extends T>? contextualActions = null, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action = {});
+}), kotlin.jvm.functions.Function0<kotlin.Unit>? navigationIcon = (kotlin.jvm.functions.Function0<? extends kotlin.Unit>)null, java.util.List<? extends T>? contextualActions = null, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> action = {});
   }
 
   public final class BottomAppBar {
@@ -211,7 +211,26 @@
   public final class TabKt {
     ctor public TabKt();
     method public static void Tab(String? text = null, androidx.ui.painting.Image? icon = null, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onSelected);
-    method public static <T> void TabRow(java.util.List<? extends T> items, int selectedIndex, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> tab);
+    method public static <T> void TabRow(java.util.List<? extends T> items, int selectedIndex, kotlin.jvm.functions.Function1<? super java.util.List<androidx.ui.material.TabRow.TabPosition>,kotlin.Unit> indicatorContainer = { tabPositions -> TabRow.IndicatorContainer(tabPositions, selectedIndex, { 
+    TabRow.Indicator()
+}) }, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> tab);
+  }
+
+  public final class TabRow {
+    method public void Indicator();
+    method public void IndicatorContainer(java.util.List<androidx.ui.material.TabRow.TabPosition> tabPositions, int selectedIndex, kotlin.jvm.functions.Function0<kotlin.Unit> indicator);
+    field public static final androidx.ui.material.TabRow! INSTANCE;
+  }
+
+  public static final class TabRow.TabPosition {
+    ctor public TabRow.TabPosition(androidx.ui.core.Dp xOffset, androidx.ui.core.Dp width, androidx.ui.core.Dp height);
+    method public androidx.ui.core.Dp component1();
+    method public androidx.ui.core.Dp component2();
+    method public androidx.ui.core.Dp component3();
+    method public androidx.ui.material.TabRow.TabPosition copy(androidx.ui.core.Dp xOffset, androidx.ui.core.Dp width, androidx.ui.core.Dp height);
+    method public androidx.ui.core.Dp getHeight();
+    method public androidx.ui.core.Dp getWidth();
+    method public androidx.ui.core.Dp getXOffset();
   }
 
   public final class TextKt {
diff --git a/ui/ui-material/integration-tests/material-demos/build.gradle b/ui/ui-material/integration-tests/material-demos/build.gradle
index 01f612f..8c60318 100644
--- a/ui/ui-material/integration-tests/material-demos/build.gradle
+++ b/ui/ui-material/integration-tests/material-demos/build.gradle
@@ -16,10 +16,12 @@
     kotlinPlugin project(path: ":compose:compose-compiler", configuration: "embeddablePlugin")
 
     implementation(KOTLIN_COMPOSE_COROUTINES)
+    implementation(KOTLIN_COMPOSE_REFLECT)
     implementation(KOTLIN_COMPOSE_STDLIB)
 
     implementation "androidx.activity:activity:1.0.0-alpha01"
     implementation "androidx.annotation:annotation:1.1.0"
+    implementation "androidx.preference:preference:1.1.0-rc01"
 
     implementation project(":compose:compose-runtime")
     implementation project(":ui:ui-core")
@@ -41,10 +43,10 @@
 }
 
 androidx {
-    name = "Crane Material Composables"
-    publish = Publish.SNAPSHOT_AND_RELEASE
+    name = "Compose Material Demos"
+    publish = Publish.NONE
     mavenVersion = LibraryVersions.UI
     mavenGroup = LibraryGroups.UI
     inceptionYear = "2019"
-    description = "This is a temporary project for Material composables."
+    description = "This is a project for Material demos."
 }
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/AndroidManifest.xml b/ui/ui-material/integration-tests/material-demos/src/main/AndroidManifest.xml
index c0b0e7d..d7dac44 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/AndroidManifest.xml
+++ b/ui/ui-material/integration-tests/material-demos/src/main/AndroidManifest.xml
@@ -146,5 +146,10 @@
                 <category android:name="androidx.ui.demos.SAMPLE_CODE" />
             </intent-filter>
         </activity>
+
+        <activity android:name=".MaterialSettingsActivity"
+                  android:label="Material Theme Settings"
+                  android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
+        </activity>
     </application>
 </manifest>
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/ic_launcher-web.png b/ui/ui-material/integration-tests/material-demos/src/main/ic_launcher-web.png
deleted file mode 100644
index 88e5f3b..0000000
--- a/ui/ui-material/integration-tests/material-demos/src/main/ic_launcher-web.png
+++ /dev/null
Binary files differ
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/AppBarActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/AppBarActivity.kt
index eb9d41b..486ff6f 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/AppBarActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/AppBarActivity.kt
@@ -16,62 +16,46 @@
 
 package androidx.ui.material.demos
 
-import android.app.Activity
-import android.os.Bundle
 import androidx.compose.Composable
 import androidx.compose.composer
 import androidx.compose.unaryPlus
 import androidx.ui.core.Text
-import androidx.ui.core.dp
-import androidx.ui.core.setContent
 import androidx.ui.layout.Column
-import androidx.ui.layout.FlexColumn
-import androidx.ui.layout.HeightSpacer
-import androidx.ui.material.MaterialTheme
+import androidx.ui.layout.MainAxisAlignment
 import androidx.ui.material.samples.SimpleBottomAppBarCenterFab
 import androidx.ui.material.samples.SimpleBottomAppBarEndFab
 import androidx.ui.material.samples.SimpleBottomAppBarNoFab
 import androidx.ui.material.samples.SimpleTopAppBar
+import androidx.ui.material.samples.SimpleTopAppBarNavIcon
 import androidx.ui.material.themeTextStyle
 import androidx.ui.painting.imageFromResource
 
-class AppBarActivity : Activity() {
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContent {
-            MaterialTheme {
-                val favouriteImage = { imageFromResource(resources, R.drawable.ic_favorite) }
-                val navigationImage = { imageFromResource(resources, R.drawable.ic_menu) }
-                FlexColumn {
-                    expanded(1f) {
-                        Column {
-                            SpacedText("TopAppBar")
-                            HeightSpacer(height = 28.dp)
-                            SimpleTopAppBar(favouriteImage, navigationImage)
-                        }
-                        Column {
-                            SpacedText("BottomAppBar - No FAB")
-                            HeightSpacer(height = 28.dp)
-                            SimpleBottomAppBarNoFab(favouriteImage, navigationImage)
-                        }
-                        Column {
-                            SpacedText("BottomAppBar - Center FAB")
-                            SimpleBottomAppBarCenterFab(favouriteImage, navigationImage)
-                        }
-                        Column {
-                            SpacedText("BottomAppBar - End FAB")
-                            SimpleBottomAppBarEndFab(favouriteImage)
-                        }
-                    }
-                }
-            }
+class AppBarActivity : MaterialDemoActivity() {
+
+    @Composable
+    override fun materialContent() {
+        val favouriteImage = { imageFromResource(resources, R.drawable.ic_favorite) }
+        val navigationImage = { imageFromResource(resources, R.drawable.ic_menu) }
+        Column(mainAxisAlignment = MainAxisAlignment.SpaceBetween) {
+            DemoText("TopAppBar")
+            SimpleTopAppBar(favouriteImage)
+
+            DemoText("TopAppBar - With navigation icon")
+            SimpleTopAppBarNavIcon(favouriteImage, navigationImage)
+
+            DemoText("BottomAppBar - No FAB")
+            SimpleBottomAppBarNoFab(favouriteImage, navigationImage)
+
+            DemoText("BottomAppBar - Center FAB")
+            SimpleBottomAppBarCenterFab(favouriteImage, navigationImage)
+
+            DemoText("BottomAppBar - End FAB")
+            SimpleBottomAppBarEndFab(favouriteImage)
         }
     }
 
     @Composable
-    private fun SpacedText(text: String) {
-        HeightSpacer(height = 12.dp)
+    private fun DemoText(text: String) {
         Text(text, style = +themeTextStyle { h6 })
-        HeightSpacer(height = 12.dp)
     }
 }
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/BottomDrawerActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/BottomDrawerActivity.kt
index 1c1147b..e69dade 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/BottomDrawerActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/BottomDrawerActivity.kt
@@ -16,21 +16,14 @@
 
 package androidx.ui.material.demos
 
-import android.app.Activity
-import android.os.Bundle
+import androidx.compose.Composable
 import androidx.compose.composer
-import androidx.ui.core.setContent
-import androidx.ui.material.MaterialTheme
 import androidx.ui.material.samples.BottomDrawerSample
 
-class BottomDrawerActivity : Activity() {
+class BottomDrawerActivity : MaterialDemoActivity() {
 
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContent {
-            MaterialTheme {
-                BottomDrawerSample()
-            }
-        }
+    @Composable
+    override fun materialContent() {
+        BottomDrawerSample()
     }
 }
\ No newline at end of file
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonActivity.kt
index b6ea1bb..e6ad89c 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonActivity.kt
@@ -32,15 +32,61 @@
 
 package androidx.ui.material.demos
 
-import android.app.Activity
-import android.os.Bundle
+import android.util.Log
+import androidx.compose.Composable
 import androidx.compose.composer
-import androidx.ui.core.setContent
+import androidx.compose.unaryPlus
+import androidx.ui.core.Text
+import androidx.ui.core.dp
+import androidx.ui.foundation.shape.border.Border
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Center
+import androidx.ui.layout.Column
+import androidx.ui.layout.MainAxisAlignment
+import androidx.ui.layout.Padding
+import androidx.ui.material.Button
+import androidx.ui.material.TransparentButton
+import androidx.ui.material.themeColor
+import androidx.ui.material.themeTextStyle
 
-class ButtonActivity : Activity() {
+class ButtonActivity : MaterialDemoActivity() {
 
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContent { ButtonDemo() }
+    @Composable
+    override fun materialContent() {
+        val onClick: () -> Unit = { Log.e("ButtonDemo", "onClick") }
+        Center {
+            Column(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+                Button(onClick = onClick, text = "LONG TEXT")
+                Button(onClick = onClick, text = "SH")
+                TransparentButton(onClick = onClick, text = "NO BACKGROUND")
+                Button(
+                    onClick = onClick,
+                    color = +themeColor { secondary },
+                    text = "SECONDARY COLOR"
+                )
+
+                TransparentButton(
+                    onClick = onClick,
+                    border = Border(Color(0xFF888888.toInt()), 1.dp),
+                    text = "OUTLINED"
+                )
+
+                val customColor = Color(0xFFFFFF00.toInt())
+                Button(
+                    onClick = onClick,
+                    text = "CUSTOM STYLE",
+                    textStyle = +themeTextStyle { body2.copy(color = customColor) })
+                Button(onClick = onClick) {
+                    Padding(padding = 16.dp) {
+                        Text(text = "CUSTOM BUTTON!")
+                    }
+                }
+
+                // TODO(Andrey): Disabled button has wrong bg and text color for now.
+                // Need to figure out where will we store their styling. Not a part of
+                // MaterialColors right now and specs are not clear about this.
+                Button(text = "DISABLED. TODO")
+            }
+        }
     }
 }
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonDemo.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonDemo.kt
deleted file mode 100644
index 35279f4..0000000
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ButtonDemo.kt
+++ /dev/null
@@ -1,76 +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.material.demos
-
-import android.util.Log
-import androidx.compose.Composable
-import androidx.compose.composer
-import androidx.compose.unaryPlus
-import androidx.ui.core.Text
-import androidx.ui.core.dp
-import androidx.ui.graphics.Color
-import androidx.ui.foundation.shape.border.Border
-import androidx.ui.layout.Center
-import androidx.ui.layout.Column
-import androidx.ui.layout.MainAxisAlignment
-import androidx.ui.layout.Padding
-import androidx.ui.material.Button
-import androidx.ui.material.MaterialTheme
-import androidx.ui.material.TransparentButton
-import androidx.ui.material.themeColor
-import androidx.ui.material.themeTextStyle
-
-@Composable
-fun ButtonDemo() {
-    MaterialTheme {
-        val onClick: () -> Unit = { Log.e("ButtonDemo", "onClick") }
-        Center {
-            Column(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
-                Button(onClick = onClick, text = "LONG TEXT")
-                Button(onClick = onClick, text = "SH")
-                TransparentButton(onClick = onClick, text = "NO BACKGROUND")
-                Button(
-                    onClick = onClick,
-                    color = +themeColor { secondary },
-                    text = "SECONDARY COLOR"
-                )
-
-                TransparentButton(
-                    onClick = onClick,
-                    border = Border(Color(0xFF888888.toInt()), 1.dp),
-                    text = "OUTLINED"
-                )
-
-                val customColor = Color(0xFFFFFF00.toInt())
-                Button(
-                    onClick = onClick,
-                    text = "CUSTOM STYLE",
-                    textStyle = +themeTextStyle { body2.copy(color = customColor) })
-                Button(onClick = onClick) {
-                    Padding(padding = 16.dp) {
-                        Text(text = "CUSTOM BUTTON!")
-                    }
-                }
-
-                // TODO(Andrey): Disabled button has wrong bg and text color for now.
-                // Need to figure out where will we store their styling. Not a part of
-                // MaterialColors right now and specs are not clear about this.
-                Button(text = "DISABLED. TODO")
-            }
-        }
-    }
-}
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/CustomShapeActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/CustomShapeActivity.kt
index 4b76cca..7d49679 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/CustomShapeActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/CustomShapeActivity.kt
@@ -16,13 +16,11 @@
 
 package androidx.ui.material.demos
 
-import android.app.Activity
 import android.graphics.drawable.ColorDrawable
 import android.os.Bundle
 import androidx.compose.Composable
 import androidx.compose.composer
 import androidx.ui.core.dp
-import androidx.ui.core.setContent
 import androidx.ui.foundation.shape.border.Border
 import androidx.ui.foundation.shape.GenericShape
 import androidx.ui.graphics.Color
@@ -32,12 +30,16 @@
 import androidx.ui.material.Button
 import androidx.ui.material.MaterialTheme
 
-class CustomShapeActivity : Activity() {
+class CustomShapeActivity : MaterialDemoActivity() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         window.setBackgroundDrawable(ColorDrawable(android.graphics.Color.WHITE))
-        setContent { CustomShapeDemo() }
+    }
+
+    @Composable
+    override fun materialContent() {
+        CustomShapeDemo()
     }
 }
 
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/DividersSpacersActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/DividersSpacersActivity.kt
index 5289f89..fba5126 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/DividersSpacersActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/DividersSpacersActivity.kt
@@ -16,20 +16,81 @@
 
 package androidx.ui.material.demos
 
-import android.app.Activity
-import android.os.Bundle
+import androidx.compose.Composable
 import androidx.compose.composer
-import androidx.ui.core.setContent
-import androidx.ui.material.MaterialTheme
+import androidx.compose.unaryPlus
+import androidx.ui.core.Text
+import androidx.ui.core.dp
+import androidx.ui.foundation.ColoredRect
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Column
+import androidx.ui.layout.Container
+import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.EdgeInsets
+import androidx.ui.layout.HeightSpacer
+import androidx.ui.layout.Row
+import androidx.ui.layout.WidthSpacer
+import androidx.ui.material.Divider
+import androidx.ui.material.themeTextStyle
 
-class DividersSpacersActivity : Activity() {
+class DividersSpacersActivity : MaterialDemoActivity() {
 
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContent {
-            MaterialTheme {
-                DividersDemo()
+    @Composable
+    override fun materialContent() {
+        DividersDemo()
+    }
+
+    @Composable
+    fun DividersDemo() {
+        val items = listOf(
+            "Lorem ipsum dolor sit amet.",
+            "Morbi ac purus eget quam dapibus cursus.",
+            "Integer viverra libero eget.",
+            "Mauris tristique arcu nec aliquam.",
+            "Vivamus euismod augue eget maximus."
+        )
+        val color = Color(0xFFE91E63.toInt())
+        val dividerColor = Color(0xFFC6C6C6.toInt())
+        val blackColor = Color.Black
+        Column {
+            Column {
+                items.forEachIndexed { index, text ->
+                    Item(text = text, color = color)
+                    if (index != items.lastIndex) {
+                        Divider(color = dividerColor, indent = ItemSize)
+                    }
+                }
+            }
+            HeightSpacer(height = 30.dp)
+            Divider(height = 2.dp, color = blackColor)
+            HeightSpacer(height = 10.dp)
+            Column {
+                items.forEach { text ->
+                    Item(text = text)
+                    Divider(color = dividerColor, height = 0.5.dp)
+                }
             }
         }
     }
+
+    @Composable
+    fun Item(text: String, color: Color? = null) {
+        val avatarSize = ItemSize - ItemPadding * 2
+        val textStyle = +themeTextStyle { body1 }
+        Container(height = ItemSize, padding = EdgeInsets(ItemPadding)) {
+            Row(crossAxisAlignment = CrossAxisAlignment.Center) {
+                if (color != null) {
+                    ColoredRect(
+                        width = avatarSize,
+                        height = avatarSize,
+                        color = color)
+                    WidthSpacer(width = ItemPadding)
+                }
+                Text(text = text, style = textStyle)
+            }
+        }
+    }
+
+    private val ItemSize = 55.dp
+    private val ItemPadding = 7.5.dp
 }
\ No newline at end of file
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/DividersSpacersDemo.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/DividersSpacersDemo.kt
deleted file mode 100644
index f1d4432..0000000
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/DividersSpacersDemo.kt
+++ /dev/null
@@ -1,88 +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.material.demos
-
-import androidx.compose.Composable
-import androidx.compose.unaryPlus
-import androidx.compose.composer
-import androidx.ui.core.Text
-import androidx.ui.core.dp
-import androidx.ui.foundation.ColoredRect
-import androidx.ui.graphics.Color
-import androidx.ui.layout.Column
-import androidx.ui.layout.Container
-import androidx.ui.layout.CrossAxisAlignment
-import androidx.ui.layout.EdgeInsets
-import androidx.ui.layout.HeightSpacer
-import androidx.ui.layout.Row
-import androidx.ui.layout.WidthSpacer
-import androidx.ui.material.Divider
-import androidx.ui.material.themeTextStyle
-
-@Composable
-fun DividersDemo() {
-    val items = listOf(
-        "Lorem ipsum dolor sit amet.",
-        "Morbi ac purus eget quam dapibus cursus.",
-        "Integer viverra libero eget.",
-        "Mauris tristique arcu nec aliquam.",
-        "Vivamus euismod augue eget maximus."
-    )
-    val color = Color(0xFFE91E63.toInt())
-    val dividerColor = Color(0xFFC6C6C6.toInt())
-    val blackColor = Color(0xFF000000.toInt())
-    Column {
-        Column {
-            items.forEachIndexed { index, text ->
-                Item(text = text, color = color)
-                if (index != items.lastIndex) {
-                    Divider(color = dividerColor, indent = ItemSize)
-                }
-            }
-        }
-        HeightSpacer(height = 30.dp)
-        Divider(height = 2.dp, color = blackColor)
-        HeightSpacer(height = 10.dp)
-        Column {
-            items.forEach { text ->
-                Item(text = text)
-                Divider(color = dividerColor, height = 0.5.dp)
-            }
-        }
-    }
-}
-
-@Composable
-fun Item(text: String, color: Color? = null) {
-    val avatarSize = ItemSize - ItemPadding * 2
-    val textStyle = +themeTextStyle { body1 }
-    Container(height = ItemSize, padding = EdgeInsets(ItemPadding)) {
-        Row(crossAxisAlignment = CrossAxisAlignment.Center) {
-            if (color != null) {
-                ColoredRect(
-                    width = avatarSize,
-                    height = avatarSize,
-                    color = color)
-                WidthSpacer(width = ItemPadding)
-            }
-            Text(text = text, style = textStyle)
-        }
-    }
-}
-
-private val ItemSize = 55.dp
-private val ItemPadding = 7.5.dp
\ No newline at end of file
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonActivity.kt
index dd2eac9..6b632f9 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonActivity.kt
@@ -16,17 +16,27 @@
 
 package androidx.ui.material.demos
 
-import android.app.Activity
-import android.os.Bundle
+import android.util.Log
+import androidx.compose.Composable
 import androidx.ui.painting.imageFromResource
 import androidx.compose.composer
-import androidx.ui.core.setContent
+import androidx.ui.layout.Center
+import androidx.ui.layout.Column
+import androidx.ui.layout.MainAxisAlignment
+import androidx.ui.material.FloatingActionButton
 
-class FloatingActionButtonActivity : Activity() {
+class FloatingActionButtonActivity : MaterialDemoActivity() {
 
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
+    @Composable
+    override fun materialContent() {
         val icon = imageFromResource(resources, R.drawable.ic_favorite)
-        setContent { FloatingActionButtonDemo(icon = icon) }
+        Center {
+            val onClick: () -> Unit = { Log.e("FABDemo", "onClick") }
+            Column(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
+                FloatingActionButton(icon = icon, onClick = onClick)
+                FloatingActionButton(text = "EXTENDED", onClick = onClick)
+                FloatingActionButton(icon = icon, text = "ADD TO FAVS", onClick = onClick)
+            }
+        }
     }
 }
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonDemo.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonDemo.kt
deleted file mode 100644
index 6405923..0000000
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/FloatingActionButtonDemo.kt
+++ /dev/null
@@ -1,41 +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.material.demos
-
-import android.util.Log
-import androidx.ui.layout.Center
-import androidx.ui.layout.Column
-import androidx.ui.layout.MainAxisAlignment
-import androidx.ui.material.FloatingActionButton
-import androidx.ui.painting.Image
-import androidx.compose.Composable
-import androidx.compose.composer
-import androidx.ui.material.MaterialTheme
-
-@Composable
-fun FloatingActionButtonDemo(icon: Image) {
-    MaterialTheme {
-        Center {
-            val onClick: () -> Unit = { Log.e("FABDemo", "onClick") }
-            Column(mainAxisAlignment = MainAxisAlignment.SpaceEvenly) {
-                FloatingActionButton(icon = icon, onClick = onClick)
-                FloatingActionButton(text = "EXTENDED", onClick = onClick)
-                FloatingActionButton(icon = icon, text = "ADD TO FAVS", onClick = onClick)
-            }
-        }
-    }
-}
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/MaterialDemoActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/MaterialDemoActivity.kt
new file mode 100644
index 0000000..9d0da33
--- /dev/null
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/MaterialDemoActivity.kt
@@ -0,0 +1,479 @@
+/*
+ * 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.demos
+
+import android.app.Activity
+import android.content.Intent
+import android.content.SharedPreferences
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuItem
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.Composable
+import androidx.compose.FrameManager
+import androidx.compose.Model
+import androidx.compose.composer
+import androidx.preference.EditTextPreference
+import androidx.preference.PreferenceFragmentCompat
+import androidx.preference.PreferenceManager.getDefaultSharedPreferences
+import androidx.ui.core.setContent
+import androidx.ui.graphics.Color
+import androidx.ui.material.MaterialColors
+import androidx.ui.material.MaterialTheme
+import kotlin.random.Random
+import kotlin.reflect.full.memberProperties
+import kotlin.reflect.full.primaryConstructor
+
+@Model
+class CurrentMaterialColors {
+    var colors = MaterialColors()
+}
+
+/**
+ * Base [Activity] for material demos. Handles generating and editing the top level [MaterialTheme].
+ * Subclasses should override [materialContent] to emit their specific demos.
+ */
+abstract class MaterialDemoActivity : Activity() {
+
+    private val currentColors = CurrentMaterialColors()
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        // Ensure we are in a frame, as this is only normally initialized after the setContent call
+        FrameManager.ensureStarted()
+        currentColors.colors = getColorsFromSharedPreferences()
+        setContent {
+            MaterialTheme(currentColors.colors) {
+                materialContent()
+            }
+        }
+    }
+
+    override fun onResume() {
+        super.onResume()
+        // Update colors in case we changed something in settings activity
+        currentColors.colors = getColorsFromSharedPreferences()
+    }
+
+    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+        menu?.add(Menu.NONE, SETTINGS, Menu.NONE, "Theme settings")
+        menu?.add(Menu.NONE, SHUFFLE, Menu.NONE, "Shuffle colors")
+        menu?.add(Menu.NONE, INVERT, Menu.NONE, "Invert color mapping")
+        menu?.add(Menu.NONE, RESET, Menu.NONE, "Reset theme to default")
+        return true
+    }
+
+    override fun onOptionsItemSelected(item: MenuItem): Boolean {
+        when (item.itemId) {
+            SETTINGS -> startActivity(Intent(this, MaterialSettingsActivity::class.java))
+            SHUFFLE -> {
+                val colors = generateMaterialColors()
+                colors.saveColors()
+                currentColors.colors = colors
+            }
+            INVERT -> {
+                // Flip all colors
+                val newPrimary = currentColors.colors.onPrimary
+                val newOnPrimary = currentColors.colors.primary
+                val newSecondary = currentColors.colors.onSecondary
+                val newOnSecondary = currentColors.colors.secondary
+                val colors = currentColors.colors.copy(
+                    primary = newPrimary,
+                    onPrimary = newOnPrimary,
+                    secondary = newSecondary,
+                    onSecondary = newOnSecondary
+                )
+                colors.saveColors()
+                currentColors.colors = colors
+            }
+            RESET -> {
+                val sharedPreferences = getDefaultSharedPreferences(this)
+                sharedPreferences.edit().clear().apply()
+                currentColors.colors = getColorsFromSharedPreferences()
+            }
+        }
+        return true
+    }
+
+    /**
+     * Returns [MaterialColors] from the values saved to [SharedPreferences]. If a given color is
+     * not present in the [SharedPreferences], its default value as defined in [MaterialColors]
+     * will be returned.
+     */
+    private fun getColorsFromSharedPreferences(): MaterialColors {
+        val sharedPreferences = getDefaultSharedPreferences(this)
+        val constructor = MaterialColors::class.primaryConstructor!!
+        val parametersToSet = constructor.parameters.mapNotNull { parameter ->
+            val savedValue = sharedPreferences.getString(parameter.name, "")
+            if (savedValue.isNullOrBlank()) {
+                null
+            } else {
+                val parsedColor = Color(java.lang.Long.parseLong(savedValue, 16).toInt())
+                parameter to parsedColor
+            }
+        }.toMap()
+        return MaterialColors::class.primaryConstructor!!.callBy(parametersToSet)
+    }
+
+    /**
+     * Persists the current [MaterialColors] to [SharedPreferences].
+     */
+    private fun MaterialColors.saveColors() {
+        forEachColorProperty { name, color ->
+            getDefaultSharedPreferences(this@MaterialDemoActivity)
+                .edit()
+                .putString(name, Integer.toHexString(color.toArgb()))
+                .apply()
+        }
+    }
+
+    /**
+     * Generates random colors for [MaterialColors.primary], [MaterialColors.onPrimary],
+     * [MaterialColors.secondary] and [MaterialColors.onSecondary] as dark-on-light or light-on-dark
+     * pairs.
+     */
+    private fun generateMaterialColors(): MaterialColors {
+        val (primary, onPrimary) = generateColorPair()
+        val (secondary, onSecondary) = generateColorPair()
+        return MaterialColors(
+            primary = primary,
+            onPrimary = onPrimary,
+            secondary = secondary,
+            onSecondary = onSecondary
+        )
+    }
+
+    /**
+     * Generate a random dark and light color from the palette, and returns either a dark-on-light
+     * or light-on-dark color pair.
+     */
+    private fun generateColorPair(): Pair<Color, Color> {
+        val darkColor = Color(DARK_PALETTE_COLORS.random().toInt())
+        val lightColor = Color(LIGHT_PALETTE_COLORS.random().toInt())
+        val isMainColorLight = Random.nextBoolean()
+        return if (isMainColorLight) {
+            (lightColor to darkColor)
+        } else {
+            (darkColor to lightColor)
+        }
+    }
+
+    /**
+     * Override this function to return the composable hierarchy that should be displayed inside the
+     * customized [MaterialTheme].
+     */
+    @Composable
+    abstract fun materialContent()
+
+    companion object {
+        private const val SETTINGS = 1
+        private const val SHUFFLE = 2
+        private const val INVERT = 3
+        private const val RESET = 4
+
+        // Colors taken from https://material.io/design/color -> 2014 Material Design color palettes
+
+        private val LIGHT_PALETTE_COLORS = listOf(
+            0xFFEF5350,
+            0xFFF44336,
+            0xFFE53935,
+            0xFFD32F2F,
+            0xFFC62828,
+            0xFFB71C1C,
+            0xFFFF5252,
+            0xFFFF1744,
+            0xFFD50000,
+            0xFFEC407A,
+            0xFFE91E63,
+            0xFFD81B60,
+            0xFFC2185B,
+            0xFFAD1457,
+            0xFF880E4F,
+            0xFFFF4081,
+            0xFFF50057,
+            0xFFC51162,
+            0xFFBA68C8,
+            0xFFAB47BC,
+            0xFF9C27B0,
+            0xFF8E24AA,
+            0xFF7B1FA2,
+            0xFF6A1B9A,
+            0xFF4A148C,
+            0xFFE040FB,
+            0xFFD500F9,
+            0xFFAA00FF,
+            0xFF9575CD,
+            0xFF7E57C2,
+            0xFF673AB7,
+            0xFF5E35B1,
+            0xFF512DA8,
+            0xFF4527A0,
+            0xFF311B92,
+            0xFF7C4DFF,
+            0xFF651FFF,
+            0xFF6200EA,
+            0xFF7986CB,
+            0xFF5C6BC0,
+            0xFF3F51B5,
+            0xFF3949AB,
+            0xFF303F9F,
+            0xFF283593,
+            0xFF1A237E,
+            0xFF536DFE,
+            0xFF3D5AFE,
+            0xFF304FFE,
+            0xFF1E88E5,
+            0xFF1976D2,
+            0xFF1565C0,
+            0xFF0D47A1,
+            0xFF448AFF,
+            0xFF2979FF,
+            0xFF2962FF,
+            0xFF0288D1,
+            0xFF0277BD,
+            0xFF01579B,
+            0xFF0091EA,
+            0xFF0097A7,
+            0xFF00838F,
+            0xFF006064,
+            0xFF009688,
+            0xFF00897B,
+            0xFF00796B,
+            0xFF00695C,
+            0xFF004D40,
+            0xFF43A047,
+            0xFF388E3C,
+            0xFF2E7D32,
+            0xFF1B5E20,
+            0xFF558B2F,
+            0xFF33691E,
+            0xFF827717,
+            0xFFE65100,
+            0xFFF4511E,
+            0xFFE64A19,
+            0xFFD84315,
+            0xFFBF360C,
+            0xFFFF3D00,
+            0xFFDD2C00,
+            0xFFA1887F,
+            0xFF8D6E63,
+            0xFF795548,
+            0xFF6D4C41,
+            0xFF5D4037,
+            0xFF4E342E,
+            0xFF3E2723,
+            0xFF757575,
+            0xFF616161,
+            0xFF424242,
+            0xFF212121,
+            0xFF78909C,
+            0xFF607D8B,
+            0xFF546E7A,
+            0xFF455A64,
+            0xFF37474F,
+            0xFF263238
+        )
+
+        private val DARK_PALETTE_COLORS = listOf(
+            0xFFFFCDD2,
+            0xFFEF9A9A,
+            0xFFE57373,
+            0xFFFF8A80,
+            0xFFF8BBD0,
+            0xFFF48FB1,
+            0xFFF06292,
+            0xFFFF80AB,
+            0xFFE1BEE7,
+            0xFFCE93D8,
+            0xFFEA80FC,
+            0xFFD1C4E9,
+            0xFFB39DDB,
+            0xFFB388FF,
+            0xFFC5CAE9,
+            0xFF9FA8DA,
+            0xFF8C9EFF,
+            0xFFBBDEFB,
+            0xFF90CAF9,
+            0xFF64B5F6,
+            0xFF42A5F5,
+            0xFF2196F3,
+            0xFF82B1FF,
+            0xFFB3E5FC,
+            0xFF81D4FA,
+            0xFF4FC3F7,
+            0xFF29B6F6,
+            0xFF03A9F4,
+            0xFF039BE5,
+            0xFF80D8FF,
+            0xFF40C4FF,
+            0xFF00B0FF,
+            0xFFB2EBF2,
+            0xFF80DEEA,
+            0xFF4DD0E1,
+            0xFF26C6DA,
+            0xFF00BCD4,
+            0xFF00ACC1,
+            0xFF84FFFF,
+            0xFF18FFFF,
+            0xFF00E5FF,
+            0xFF00B8D4,
+            0xFFB2DFDB,
+            0xFF80CBC4,
+            0xFF4DB6AC,
+            0xFF26A69A,
+            0xFFA7FFEB,
+            0xFF64FFDA,
+            0xFF1DE9B6,
+            0xFF00BFA5,
+            0xFFC8E6C9,
+            0xFFA5D6A7,
+            0xFF81C784,
+            0xFF66BB6A,
+            0xFF4CAF50,
+            0xFFB9F6CA,
+            0xFF69F0AE,
+            0xFF00E676,
+            0xFF00C853,
+            0xFFDCEDC8,
+            0xFFC5E1A5,
+            0xFFAED581,
+            0xFF9CCC65,
+            0xFF8BC34A,
+            0xFF7CB342,
+            0xFF689F38,
+            0xFFCCFF90,
+            0xFFB2FF59,
+            0xFF76FF03,
+            0xFF64DD17,
+            0xFFF0F4C3,
+            0xFFE6EE9C,
+            0xFFDCE775,
+            0xFFD4E157,
+            0xFFCDDC39,
+            0xFFC0CA33,
+            0xFFAFB42B,
+            0xFF9E9D24,
+            0xFFF4FF81,
+            0xFFEEFF41,
+            0xFFC6FF00,
+            0xFFAEEA00,
+            0xFFFFF9C4,
+            0xFFFFF59D,
+            0xFFFFF176,
+            0xFFFFEE58,
+            0xFFFFEB3B,
+            0xFFFDD835,
+            0xFFFBC02D,
+            0xFFF9A825,
+            0xFFF57F17,
+            0xFFFFFF8D,
+            0xFFFFFF00,
+            0xFFFFEA00,
+            0xFFFFD600,
+            0xFFFFECB3,
+            0xFFFFE082,
+            0xFFFFD54F,
+            0xFFFFCA28,
+            0xFFFFC107,
+            0xFFFFB300,
+            0xFFFFA000,
+            0xFFFF8F00,
+            0xFFFF6F00,
+            0xFFFFE57F,
+            0xFFFFD740,
+            0xFFFFC400,
+            0xFFFFAB00,
+            0xFFFFE0B2,
+            0xFFFFCC80,
+            0xFFFFB74D,
+            0xFFFFA726,
+            0xFFFF9800,
+            0xFFFB8C00,
+            0xFFF57C00,
+            0xFFEF6C00,
+            0xFFFFD180,
+            0xFFFFAB40,
+            0xFFFF9100,
+            0xFFFF6D00,
+            0xFFFFCCBC,
+            0xFFFFAB91,
+            0xFFFF8A65,
+            0xFFFF7043,
+            0xFFFF5722,
+            0xFFFF9E80,
+            0xFFFF6E40,
+            0xFFD7CCC8,
+            0xFFBCAAA4,
+            0xFFF5F5F5,
+            0xFFEEEEEE,
+            0xFFE0E0E0,
+            0xFFBDBDBD,
+            0xFF9E9E9E,
+            0xFFCFD8DC,
+            0xFFB0BEC5,
+            0xFF90A4AE,
+            0xFFFFFFFF
+        )
+    }
+}
+
+/**
+ * Shell [AppCompatActivity] around [SettingsFragment], as we need a FragmentActivity subclass
+ * to host the [SettingsFragment].
+ */
+class MaterialSettingsActivity : AppCompatActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        supportFragmentManager
+            .beginTransaction()
+            .replace(android.R.id.content, SettingsFragment())
+            .commit()
+    }
+
+    class SettingsFragment : PreferenceFragmentCompat() {
+        override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+            val context = preferenceManager.context
+            val screen = preferenceManager.createPreferenceScreen(context)
+            // Create new MaterialColors to resolve defaults
+            MaterialColors().forEachColorProperty { name, color ->
+                val preference = EditTextPreference(context)
+                preference.key = name
+                preference.title = name
+                // set the default value to be the default for MaterialColors
+                preference.setDefaultValue(Integer.toHexString(color.toArgb()))
+                preference.summaryProvider = EditTextPreference.SimpleSummaryProvider.getInstance()
+                screen.addPreference(preference)
+            }
+            preferenceScreen = screen
+        }
+    }
+}
+
+/**
+ * Iterates over each color present in a given [MaterialColors].
+ *
+ * @param action the action to take on each property, where [name] is the name of the property,
+ * such as 'primary' for [MaterialColors.primary], and [color] is the resolved [Color] of the
+ * property.
+ */
+private fun MaterialColors.forEachColorProperty(action: (name: String, color: Color) -> Unit) {
+    MaterialColors::class.memberProperties.forEach { property ->
+        val name = property.name
+        val color = property.get(this) as Color
+        action(name, color)
+    }
+}
\ No newline at end of file
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ModalDrawerActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ModalDrawerActivity.kt
index 1938d75..8df119a 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ModalDrawerActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ModalDrawerActivity.kt
@@ -16,21 +16,14 @@
 
 package androidx.ui.material.demos
 
-import android.app.Activity
-import android.os.Bundle
+import androidx.compose.Composable
 import androidx.compose.composer
-import androidx.ui.core.setContent
-import androidx.ui.material.MaterialTheme
 import androidx.ui.material.samples.ModalDrawerSample
 
-class ModalDrawerActivity : Activity() {
+class ModalDrawerActivity : MaterialDemoActivity() {
 
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContent {
-            MaterialTheme {
-                ModalDrawerSample()
-            }
-        }
+    @Composable
+    override fun materialContent() {
+        ModalDrawerSample()
     }
 }
\ No newline at end of file
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt
index 4ed72d1..d3cdaaa 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt
@@ -16,8 +16,6 @@
 
 package androidx.ui.material.demos
 
-import android.app.Activity
-import android.os.Bundle
 import android.os.Handler
 import androidx.ui.layout.FlexColumn
 import androidx.ui.layout.MainAxisAlignment.SpaceEvenly
@@ -31,17 +29,12 @@
 import androidx.compose.onActive
 import androidx.compose.onDispose
 import androidx.compose.unaryPlus
-import androidx.ui.core.setContent
-import androidx.ui.material.MaterialTheme
 
-class ProgressIndicatorActivity : Activity() {
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContent {
-            MaterialTheme {
-                ProgressIndicatorDemo()
-            }
-        }
+class ProgressIndicatorActivity : MaterialDemoActivity() {
+
+    @Composable
+    override fun materialContent() {
+        ProgressIndicatorDemo()
     }
 }
 
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 514813f..a0eaee6 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
@@ -16,19 +16,165 @@
 
 package androidx.ui.material.demos
 
-import android.app.Activity
-import android.os.Bundle
+import androidx.compose.Composable
 import androidx.compose.composer
-import androidx.ui.core.setContent
-import androidx.ui.material.MaterialTheme
+import androidx.compose.memo
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.ui.core.Text
+import androidx.ui.core.dp
+import androidx.ui.foundation.selection.ToggleableState
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Column
+import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.EdgeInsets
+import androidx.ui.layout.FlexSize
+import androidx.ui.layout.MainAxisAlignment
+import androidx.ui.layout.Padding
+import androidx.ui.layout.Row
+import androidx.ui.material.Checkbox
+import androidx.ui.material.RadioButton
+import androidx.ui.material.RadioGroup
+import androidx.ui.material.Switch
+import androidx.ui.material.TriStateCheckbox
+import androidx.ui.material.surface.Surface
+import androidx.ui.material.themeTextStyle
 
-open class SelectionControlsActivity : Activity() {
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContent {
-            MaterialTheme {
-                SelectionsControlsDemo()
+class SelectionControlsActivity : MaterialDemoActivity() {
+    private val customColor = Color(0xFFFF5722.toInt())
+    private val customColor2 = Color(0xFFE91E63.toInt())
+    private val customColor3 = Color(0xFF607D8B.toInt())
+
+    @Composable
+    override fun materialContent() {
+        val headerStyle = +themeTextStyle { h6 }
+        val padding = EdgeInsets(10.dp)
+
+        Surface(color = Color.White) {
+            Padding(padding = padding) {
+                Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+                    Text(text = "Checkbox", style = headerStyle)
+                    Padding(padding = padding) {
+                        CheckboxDemo()
+                    }
+                    Text(text = "Switch", style = headerStyle)
+                    Padding(padding = padding) {
+                        SwitchDemo()
+                    }
+                    Text(text = "RadioButton", style = headerStyle)
+                    Padding(padding = padding) {
+                        RadioButtonDemo()
+                    }
+                    Text(text = "Radio group :: Default usage", style = headerStyle)
+                    Padding(padding = padding) {
+                        DefaultRadioGroup()
+                    }
+                    Text(text = "Radio group :: Custom usage", style = headerStyle)
+                    Padding(padding = padding) {
+                        CustomRadioGroup()
+                    }
+                }
             }
         }
     }
+
+    @Composable
+    fun DefaultRadioGroup() {
+        val radioOptions = listOf("Calls", "Missed", "Friends")
+        val (selectedOption, onOptionSelected) = +state { radioOptions[0] }
+        RadioGroup(
+            options = radioOptions,
+            selectedOption = selectedOption,
+            onSelectedChange = onOptionSelected,
+            radioColor = customColor2
+        )
+    }
+
+    @Composable
+    fun CustomRadioGroup() {
+        val radioOptions = listOf("Disagree", "Neutral", "Agree")
+        val (selectedOption, onOptionSelected) = +state { radioOptions[0] }
+        val textStyle = +themeTextStyle { subtitle1 }
+
+        RadioGroup {
+            Row(mainAxisSize = FlexSize.Min) {
+                radioOptions.forEach { text ->
+                    val selected = text == selectedOption
+                    RadioGroupItem(
+                        selected = selected,
+                        onSelect = { onOptionSelected(text) }) {
+                        Padding(padding = 10.dp) {
+                            Column {
+                                RadioButton(
+                                    selected = selected,
+                                    onSelect = { onOptionSelected(text) })
+                                Text(text = text, style = textStyle)
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Composable
+    fun CheckboxDemo() {
+        Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+            val (state, onStateChange) = +state { true }
+            val (state2, onStateChange2) = +state { true }
+            val (state3, onStateChange3) = +state { true }
+            val parentState = +memo(state, state2, state3) {
+                if (state && state2 && state3) ToggleableState.Checked
+                else if (!state && !state2 && !state3) ToggleableState.Unchecked
+                else ToggleableState.Indeterminate
+            }
+            val onParentClick = {
+                val s = parentState != ToggleableState.Checked
+                onStateChange(s)
+                onStateChange2(s)
+                onStateChange3(s)
+            }
+            Row {
+                TriStateCheckbox(value = parentState, onClick = onParentClick)
+                Text(text = "This is parent TriStateCheckbox", style = +themeTextStyle { body1 })
+            }
+            Padding(left = 10.dp) {
+                Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+                    Checkbox(state, onStateChange, customColor)
+                    Checkbox(state2, onStateChange2, customColor2)
+                    Checkbox(state3, onStateChange3, customColor3)
+                }
+            }
+        }
+    }
+
+    @Composable
+    fun SwitchDemo() {
+        Row(
+            mainAxisAlignment = MainAxisAlignment.SpaceAround,
+            mainAxisSize = FlexSize.Min
+        ) {
+            val (checked, onChecked) = +state { false }
+            val (checked2, onChecked2) = +state { false }
+            val (checked3, onChecked3) = +state { true }
+            val (checked4, onChecked4) = +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)
+        }
+    }
+
+    @Composable
+    fun RadioButtonDemo() {
+        Row(
+            mainAxisAlignment = MainAxisAlignment.SpaceAround,
+            mainAxisSize = FlexSize.Min
+        ) {
+            RadioButton(selected = true, onSelect = null)
+            RadioButton(selected = false, onSelect = null)
+            RadioButton(selected = true, color = customColor, onSelect = null)
+            RadioButton(selected = false, color = customColor, onSelect = null)
+        }
+    }
 }
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionsControlsDemo.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionsControlsDemo.kt
deleted file mode 100644
index 38ceab73..0000000
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/SelectionsControlsDemo.kt
+++ /dev/null
@@ -1,181 +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.material.demos
-
-import androidx.compose.Composable
-import androidx.compose.composer
-import androidx.compose.memo
-import androidx.compose.state
-import androidx.compose.unaryPlus
-import androidx.ui.core.Text
-import androidx.ui.core.dp
-import androidx.ui.foundation.selection.ToggleableState
-import androidx.ui.foundation.selection.ToggleableState.Checked
-import androidx.ui.foundation.selection.ToggleableState.Unchecked
-import androidx.ui.graphics.Color
-import androidx.ui.layout.Column
-import androidx.ui.layout.CrossAxisAlignment
-import androidx.ui.layout.EdgeInsets
-import androidx.ui.layout.FlexSize
-import androidx.ui.layout.MainAxisAlignment
-import androidx.ui.layout.Padding
-import androidx.ui.layout.Row
-import androidx.ui.material.Checkbox
-import androidx.ui.material.RadioButton
-import androidx.ui.material.RadioGroup
-import androidx.ui.material.Switch
-import androidx.ui.material.surface.Surface
-import androidx.ui.material.themeTextStyle
-import androidx.ui.material.TriStateCheckbox
-
-private val customColor = Color(0xFFFF5722.toInt())
-private val customColor2 = Color(0xFFE91E63.toInt())
-private val customColor3 = Color(0xFF607D8B.toInt())
-
-@Composable
-fun SelectionsControlsDemo() {
-
-    val headerStyle = +themeTextStyle { h6 }
-    val padding = EdgeInsets(10.dp)
-
-    Surface {
-        Padding(padding = padding) {
-            Column(crossAxisAlignment = CrossAxisAlignment.Start) {
-                Text(text = "Checkbox", style = headerStyle)
-                Padding(padding = padding) {
-                    CheckboxDemo()
-                }
-                Text(text = "Switch", style = headerStyle)
-                Padding(padding = padding) {
-                    SwitchDemo()
-                }
-                Text(text = "RadioButton", style = headerStyle)
-                Padding(padding = padding) {
-                    RadioButtonDemo()
-                }
-                Text(text = "Radio group :: Default usage", style = headerStyle)
-                Padding(padding = padding) {
-                    DefaultRadioGroup()
-                }
-                Text(text = "Radio group :: Custom usage", style = headerStyle)
-                Padding(padding = padding) {
-                    CustomRadioGroup()
-                }
-            }
-        }
-    }
-}
-
-@Composable
-fun DefaultRadioGroup() {
-    val radioOptions = listOf("Calls", "Missed", "Friends")
-    val (selectedOption, onOptionSelected) = +state { radioOptions[0] }
-    RadioGroup(
-        options = radioOptions,
-        selectedOption = selectedOption,
-        onSelectedChange = onOptionSelected,
-        radioColor = customColor2
-    )
-}
-
-@Composable
-fun CustomRadioGroup() {
-    val radioOptions = listOf("Disagree", "Neutral", "Agree")
-    val (selectedOption, onOptionSelected) = +state { radioOptions[0] }
-    val textStyle = +themeTextStyle { subtitle1 }
-
-    RadioGroup {
-        Row(mainAxisSize = FlexSize.Min) {
-            radioOptions.forEach { text ->
-                val selected = text == selectedOption
-                RadioGroupItem(
-                    selected = selected,
-                    onSelect = { onOptionSelected(text) }) {
-                    Padding(padding = 10.dp) {
-                        Column {
-                            RadioButton(
-                                selected = selected,
-                                onSelect = { onOptionSelected(text) })
-                            Text(text = text, style = textStyle)
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-@Composable
-fun CheckboxDemo() {
-    Column(crossAxisAlignment = CrossAxisAlignment.Start) {
-        val (state, onStateChange) = +state { true }
-        val (state2, onStateChange2) = +state { true }
-        val (state3, onStateChange3) = +state { true }
-        val parentState = +memo(state, state2, state3) {
-            if (state && state2 && state3) ToggleableState.Checked
-            else if (!state && !state2 && !state3) ToggleableState.Unchecked
-            else ToggleableState.Indeterminate
-        }
-        val onParentClick = {
-            val s = parentState != Checked
-            onStateChange(s)
-            onStateChange2(s)
-            onStateChange3(s)
-        }
-        Row {
-            TriStateCheckbox(value = parentState, onClick = onParentClick)
-            Text(text = "This is parent TriStateCheckbox", style = +themeTextStyle { body1 })
-        }
-        Padding(left = 10.dp) {
-            Column(crossAxisAlignment = CrossAxisAlignment.Start) {
-                Checkbox(state, onStateChange, customColor)
-                Checkbox(state2, onStateChange2, customColor2)
-                Checkbox(state3, onStateChange3, customColor3)
-            }
-        }
-    }
-}
-
-@Composable
-fun SwitchDemo() {
-    Row(
-        mainAxisAlignment = MainAxisAlignment.SpaceAround,
-        mainAxisSize = FlexSize.Min
-    ) {
-        val (checked, onChecked) = +state { false }
-        val (checked2, onChecked2) = +state { false }
-        val (checked3, onChecked3) = +state { true }
-        val (checked4, onChecked4) = +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)
-    }
-}
-
-@Composable
-fun RadioButtonDemo() {
-    Row(
-        mainAxisAlignment = MainAxisAlignment.SpaceAround,
-        mainAxisSize = FlexSize.Min
-    ) {
-        RadioButton(selected = true, onSelect = null)
-        RadioButton(selected = false, onSelect = null)
-        RadioButton(selected = true, color = customColor, onSelect = null)
-        RadioButton(selected = false, color = customColor, onSelect = null)
-    }
-}
\ No newline at end of file
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/StaticDrawerActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/StaticDrawerActivity.kt
index 8ccc222..e8c827c 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/StaticDrawerActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/StaticDrawerActivity.kt
@@ -16,21 +16,14 @@
 
 package androidx.ui.material.demos
 
-import android.app.Activity
-import android.os.Bundle
+import androidx.compose.Composable
 import androidx.compose.composer
-import androidx.ui.core.setContent
-import androidx.ui.material.MaterialTheme
 import androidx.ui.material.samples.StaticDrawerSample
 
-class StaticDrawerActivity : Activity() {
+class StaticDrawerActivity : MaterialDemoActivity() {
 
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContent {
-            MaterialTheme {
-                StaticDrawerSample()
-            }
-        }
+    @Composable
+    override fun materialContent() {
+        StaticDrawerSample()
     }
 }
\ No newline at end of file
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/TabActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/TabActivity.kt
index 85d1e64..0a22493 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/TabActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/TabActivity.kt
@@ -16,37 +16,48 @@
 
 package androidx.ui.material.demos
 
-import android.app.Activity
-import android.os.Bundle
+import androidx.compose.Composable
 import androidx.compose.composer
-import androidx.ui.core.setContent
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Center
 import androidx.ui.layout.FlexColumn
-import androidx.ui.material.MaterialTheme
-import androidx.ui.material.samples.CustomTabs
+import androidx.ui.material.Button
+import androidx.ui.material.samples.FancyIndicatorContainerTabs
+import androidx.ui.material.samples.FancyIndicatorTabs
+import androidx.ui.material.samples.FancyTabs
 import androidx.ui.material.samples.IconTabs
 import androidx.ui.material.samples.TextAndIconTabs
 import androidx.ui.material.samples.TextTabs
 import androidx.ui.painting.imageFromResource
 
-class TabActivity : Activity() {
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContent {
-            MaterialTheme {
-                val favouriteImage = imageFromResource(resources, R.drawable.ic_favorite)
-                FlexColumn {
-                    expanded(flex = 1f) {
-                        TextTabs()
-                    }
-                    expanded(flex = 1f) {
-                        IconTabs(favouriteImage)
-                    }
-                    expanded(flex = 1f) {
-                        TextAndIconTabs(favouriteImage)
-                    }
-                    expanded(flex = 1f) {
-                        CustomTabs()
-                    }
+class TabActivity : MaterialDemoActivity() {
+
+    @Composable
+    override fun materialContent() {
+        val favouriteImage = imageFromResource(resources, R.drawable.ic_favorite)
+        FlexColumn {
+            val showingSimple = +state { true }
+            val buttonText = "Show ${if (showingSimple.value) "custom" else "simple"} tabs"
+
+            expanded(flex = 1f) {
+                if (showingSimple.value) {
+                    TextTabs()
+                    IconTabs(favouriteImage)
+                    TextAndIconTabs(favouriteImage)
+                } else {
+                    FancyTabs()
+                    FancyIndicatorTabs()
+                    FancyIndicatorContainerTabs()
+                }
+            }
+
+            expanded(flex = 1f) {
+                Center {
+                    Button(color = Color.Cyan, text = buttonText, onClick = {
+                        showingSimple.value = !showingSimple.value
+                    })
                 }
             }
         }
diff --git a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/TextActivity.kt b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/TextActivity.kt
index 35a660e..afde748 100644
--- a/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/TextActivity.kt
+++ b/ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/TextActivity.kt
@@ -16,26 +16,20 @@
 
 package androidx.ui.material.demos
 
-import android.app.Activity
-import android.os.Bundle
+import androidx.compose.Composable
 import androidx.ui.core.Text
 import androidx.ui.material.themeTextStyle
 import androidx.ui.graphics.Color
 import androidx.compose.composer
 import androidx.compose.unaryPlus
-import androidx.ui.core.setContent
-import androidx.ui.material.MaterialTheme
 
-open class TextActivity : Activity() {
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        setContent {
-            MaterialTheme {
-                val textColor = Color(0xFFFF0000.toInt())
-                Text(
-                    text = "Hello",
-                    style = +themeTextStyle { h1.copy(color = textColor) })
-            }
-        }
+open class TextActivity : MaterialDemoActivity() {
+
+    @Composable
+    override fun materialContent() {
+        val textColor = Color(0xFFFF0000.toInt())
+        Text(
+            text = "Hello",
+            style = +themeTextStyle { h1.copy(color = textColor) })
     }
 }
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 37f2905..e80124f 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
@@ -19,7 +19,6 @@
 import androidx.compose.Children
 import androidx.compose.Composable
 import androidx.compose.composer
-import androidx.ui.core.CurrentTextStyleProvider
 import androidx.ui.core.sp
 import androidx.ui.text.font.FontWeight
 import androidx.ui.text.font.FontFamily
@@ -41,7 +40,7 @@
     val colors = MaterialColors(
         primary = rallyGreen,
         surface = Color(0xFF26282F.toInt()),
-        onSurface = Color(0xFFFFFFFF.toInt())
+        onSurface = Color.White
     )
     val typography = MaterialTypography(
         h1 = TextStyle(fontFamily = FontFamily("RobotoCondensed"),
@@ -86,10 +85,6 @@
 
     )
     MaterialTheme(colors = colors, typography = typography) {
-        // TODO: remove this when surface auto-sets the text color
-        val value = TextStyle(color = Color(0xFFFFFFFF.toInt()))
-        CurrentTextStyleProvider(value = value) {
-            children()
-        }
+        children()
     }
 }
\ No newline at end of file
diff --git a/ui/ui-material/integration-tests/samples/build.gradle b/ui/ui-material/integration-tests/samples/build.gradle
index 8dec260..81fe717 100644
--- a/ui/ui-material/integration-tests/samples/build.gradle
+++ b/ui/ui-material/integration-tests/samples/build.gradle
@@ -33,6 +33,7 @@
     implementation project(":annotation:annotation-sampled")
 
     implementation project(":compose:compose-runtime")
+    implementation project(":ui:ui-animation")
     implementation project(":ui:ui-core")
     implementation project(":ui:ui-foundation")
     implementation project(":ui:ui-framework")
diff --git a/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/AppBarSamples.kt b/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/AppBarSamples.kt
index 369ca53..8074eb7 100644
--- a/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/AppBarSamples.kt
+++ b/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/AppBarSamples.kt
@@ -28,9 +28,24 @@
 import androidx.ui.painting.Image
 
 @Suppress("UNUSED_VARIABLE")
+@Composable
+fun SimpleTopAppBar(getMyActionImage: () -> Image) {
+    val someActionImage: Image = getMyActionImage()
+    val contextualActions = listOf("Action 1" to someActionImage, "action 2" to someActionImage)
+
+    TopAppBar(
+        title = { Text("Simple TopAppBar") },
+        contextualActions = contextualActions
+    ) { actionData ->
+        val (actionTitle, actionImage) = actionData
+        AppBarIcon(actionImage) { /* doSomething()*/ }
+    }
+}
+
+@Suppress("UNUSED_VARIABLE")
 @Sampled
 @Composable
-fun SimpleTopAppBar(getMyActionImage: () -> Image, getMyNavigationImage: () -> Image) {
+fun SimpleTopAppBarNavIcon(getMyActionImage: () -> Image, getMyNavigationImage: () -> Image) {
     val someActionImage: Image = getMyActionImage()
     val someNavigationImage: Image = getMyNavigationImage()
 
diff --git a/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/TabSamples.kt b/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/TabSamples.kt
index 5982b39..058f62f 100644
--- a/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/TabSamples.kt
+++ b/ui/ui-material/integration-tests/samples/src/main/java/androidx/ui/material/samples/TabSamples.kt
@@ -16,17 +16,26 @@
 
 package androidx.ui.material.samples
 
+import androidx.animation.ColorPropKey
+import androidx.animation.DpPropKey
+import androidx.animation.transitionDefinition
 import androidx.annotation.Sampled
 import androidx.compose.Composable
 import androidx.compose.composer
+import androidx.compose.memo
 import androidx.compose.state
 import androidx.compose.unaryPlus
+import androidx.ui.animation.Transition
 import androidx.ui.core.Text
 import androidx.ui.core.dp
-import androidx.ui.core.sp
 import androidx.ui.foundation.ColoredRect
 import androidx.ui.foundation.selection.MutuallyExclusiveSetItem
+import androidx.ui.foundation.shape.border.Border
+import androidx.ui.foundation.shape.border.DrawBorder
+import androidx.ui.foundation.shape.corner.CornerSizes
+import androidx.ui.foundation.shape.corner.RoundedCornerShape
 import androidx.ui.graphics.Color
+import androidx.ui.layout.Alignment
 import androidx.ui.layout.Center
 import androidx.ui.layout.Column
 import androidx.ui.layout.Container
@@ -35,8 +44,8 @@
 import androidx.ui.layout.Padding
 import androidx.ui.material.Tab
 import androidx.ui.material.TabRow
+import androidx.ui.material.themeTextStyle
 import androidx.ui.painting.Image
-import androidx.ui.text.TextStyle
 
 @Sampled
 @Composable
@@ -53,7 +62,7 @@
             Center {
                 Text(
                     text = "Text tab ${state.value + 1} selected",
-                    style = TextStyle(fontSize = 10.sp)
+                    style = +themeTextStyle { body1 }
                 )
             }
         }
@@ -74,7 +83,7 @@
             Center {
                 Text(
                     text = "Icon tab ${state.value + 1} selected",
-                    style = TextStyle(fontSize = 10.sp)
+                    style = +themeTextStyle { body1 }
                 )
             }
         }
@@ -99,7 +108,7 @@
             Center {
                 Text(
                     text = "Text and icon tab ${state.value + 1} selected",
-                    style = TextStyle(fontSize = 10.sp)
+                    style = +themeTextStyle { body1 }
                 )
             }
         }
@@ -108,7 +117,7 @@
 
 @Sampled
 @Composable
-fun CustomTabs() {
+fun FancyTabs() {
     val state = +state { 0 }
     val titles = listOf("TAB 1", "TAB 2", "TAB 3")
     FlexColumn {
@@ -124,8 +133,73 @@
         flexible(flex = 1f) {
             Center {
                 Text(
-                    text = "Custom tab ${state.value + 1} selected",
-                    style = TextStyle(fontSize = 10.sp)
+                    text = "Fancy tab ${state.value + 1} selected",
+                    style = +themeTextStyle { body1 }
+                )
+            }
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun FancyIndicatorTabs() {
+    val state = +state { 0 }
+    val titles = listOf("TAB 1", "TAB 2", "TAB 3")
+
+    // Reuse the default transition, and provide our custom indicator as its child
+    val indicatorContainer = @Composable { tabPositions: List<TabRow.TabPosition> ->
+        TabRow.IndicatorContainer(tabPositions = tabPositions, selectedIndex = state.value) {
+            FancyIndicator(Color.White)
+        }
+    }
+
+    FlexColumn {
+        inflexible {
+            TabRow(
+                items = titles,
+                selectedIndex = state.value,
+                indicatorContainer = indicatorContainer
+            ) { index, text ->
+                Tab(text = text, selected = state.value == index) { state.value = index }
+            }
+        }
+        flexible(flex = 1f) {
+            Center {
+                Text(
+                    text = "Fancy indicator tab ${state.value + 1} selected",
+                    style = +themeTextStyle { body1 }
+                )
+            }
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun FancyIndicatorContainerTabs() {
+    val state = +state { 0 }
+    val titles = listOf("TAB 1", "TAB 2", "TAB 3")
+
+    val indicatorContainer = @Composable { tabPositions: List<TabRow.TabPosition> ->
+        FancyIndicatorContainer(tabPositions = tabPositions, selectedIndex = state.value)
+    }
+
+    FlexColumn {
+        inflexible {
+            TabRow(
+                items = titles,
+                selectedIndex = state.value,
+                indicatorContainer = indicatorContainer
+            ) { index, text ->
+                Tab(text = text, selected = state.value == index) { state.value = index }
+            }
+        }
+        flexible(flex = 1f) {
+            Center {
+                Text(
+                    text = "Fancy transition tab ${state.value + 1} selected",
+                    style = +themeTextStyle { body1 }
                 )
             }
         }
@@ -142,7 +216,77 @@
                 val color = if (selected) Color.Red else Color.Gray
                 ColoredRect(height = 10.dp, width = 10.dp, color = color)
                 Padding(5.dp) {
-                    Text(text = title, style = TextStyle(fontSize = 10.sp))
+                    Text(text = title, style = +themeTextStyle { body1 })
+                }
+            }
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun FancyIndicator(color: Color) {
+    // Draws a rounded rectangular with border around the Tab, with a 5.dp padding from the edges
+    // Color is passed in as a parameter [color]
+    Padding(5.dp) {
+        Container(expanded = true) {
+            DrawBorder(RoundedCornerShape(CornerSizes(5.dp)), Border(color, 2.dp))
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun FancyIndicatorContainer(tabPositions: List<TabRow.TabPosition>, selectedIndex: Int) {
+    val indicatorStart = +memo { DpPropKey() }
+    val indicatorEnd = +memo { DpPropKey() }
+    val indicatorColor = +memo { ColorPropKey() }
+
+    val colors = listOf(Color.Yellow, Color.Red, Color.Green)
+    val transitionDefinition =
+        +memo {
+            transitionDefinition {
+                tabPositions.forEachIndexed { index, position ->
+                    state(index) {
+                        this[indicatorStart] = position.xOffset
+                        this[indicatorEnd] = position.xOffset + position.width
+                        this[indicatorColor] = colors[index]
+                    }
+                }
+                repeat(tabPositions.size) { from ->
+                    repeat(tabPositions.size) { to ->
+                        if (from != to) {
+                            transition(fromState = from, toState = to) {
+                                // Handle directionality here, if we are moving to the right, we
+                                // want the right side of the indicator to move faster, if we are
+                                // moving to the left, we want the left side to move faster.
+                                val startStiffness = if (from < to) 50f else 1000f
+                                val endStiffness = if (from < to) 1000f else 50f
+                                indicatorStart using physics {
+                                    dampingRatio = 1f
+                                    stiffness = startStiffness
+                                }
+                                indicatorEnd using physics {
+                                    dampingRatio = 1f
+                                    stiffness = endStiffness
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+    // Fill up the entire TabRow with this container, and place children at the left so we can use
+    // Padding to set the 'offset'
+    Container(expanded = true, alignment = Alignment.BottomLeft) {
+        Transition(transitionDefinition, selectedIndex) { state ->
+            val offset = state[indicatorStart]
+            val width = state[indicatorEnd] - state[indicatorStart]
+            Padding(left = offset) {
+                Container(width = width) {
+                    // Pass the current color to the indicator
+                    FancyIndicator(state[indicatorColor])
                 }
             }
         }
diff --git a/ui/ui-material/src/androidTest/java/androidx/ui/material/AppBarTest.kt b/ui/ui-material/src/androidTest/java/androidx/ui/material/AppBarTest.kt
index edec03a..de0c637 100644
--- a/ui/ui-material/src/androidTest/java/androidx/ui/material/AppBarTest.kt
+++ b/ui/ui-material/src/androidTest/java/androidx/ui/material/AppBarTest.kt
@@ -76,7 +76,7 @@
     }
 
     @Test
-    fun topAppBar_defaultPositioning() {
+    fun topAppBar_default_positioning() {
         var appBarCoords: LayoutCoordinates? = null
         var navigationIconCoords: LayoutCoordinates? = null
         var titleCoords: LayoutCoordinates? = null
@@ -113,15 +113,14 @@
         }
 
         withDensity(composeTestRule.density) {
-            // Navigation icon should be at the beginning
+            // Navigation icon should be 16.dp from the start
             val navigationIconPositionX = navigationIconCoords!!.localToGlobal(PxPosition.Origin).x
             val navigationIconExpectedPositionX = 16.dp.toIntPx().toPx()
             Truth.assertThat(navigationIconPositionX).isEqualTo(navigationIconExpectedPositionX)
 
-            // Title should be next
+            // Title should be 72.dp from the start
             val titlePositionX = titleCoords!!.localToGlobal(PxPosition.Origin).x
-            val titleExpectedPositionX =
-                navigationIconPositionX + navigationIconCoords!!.size.width + 32.dp.toIntPx()
+            val titleExpectedPositionX = 72.dp.toIntPx().toPx()
             Truth.assertThat(titlePositionX).isEqualTo(titleExpectedPositionX)
 
             // Action should be placed at the end
@@ -133,6 +132,49 @@
     }
 
     @Test
+    fun topAppBar_noNavigationIcon_positioning() {
+        var appBarCoords: LayoutCoordinates? = null
+        var titleCoords: LayoutCoordinates? = null
+        var actionCoords: LayoutCoordinates? = null
+        composeTestRule.setMaterialContent {
+            Container {
+                OnChildPositioned(onPositioned = { coords ->
+                    appBarCoords = coords
+                }) {
+                    TopAppBar(
+                        title = {
+                            OnChildPositioned(onPositioned = { coords ->
+                                titleCoords = coords
+                            }) {
+                                Text("title")
+                            }
+                        },
+                        contextualActions = createImageList(1),
+                        action = {
+                            OnChildPositioned(onPositioned = { coords ->
+                                actionCoords = coords
+                            }) { it() }
+                        }
+                    )
+                }
+            }
+        }
+
+        withDensity(composeTestRule.density) {
+            // Title should now be placed 16.dp from the start, as there is no navigation icon
+            val titlePositionX = titleCoords!!.localToGlobal(PxPosition.Origin).x
+            val titleExpectedPositionX = 16.dp.toIntPx().toPx()
+            Truth.assertThat(titlePositionX).isEqualTo(titleExpectedPositionX)
+
+            // Action should still be placed at the end
+            val actionPositionX = actionCoords!!.localToGlobal(PxPosition.Origin).x
+            val actionExpectedPositionX =
+                appBarCoords!!.size.width - 16.dp.toIntPx() - 24.dp.toIntPx()
+            Truth.assertThat(actionPositionX).isEqualTo(actionExpectedPositionX)
+        }
+    }
+
+    @Test
     fun topAppBar_oneAction() {
         val tag = "action"
         val numberOfActions = 1
@@ -199,6 +241,36 @@
     }
 
     @Test
+    fun bottomAppBar_noNavigationIcon_positioning() {
+        var appBarCoords: LayoutCoordinates? = null
+        var actionCoords: LayoutCoordinates? = null
+        composeTestRule.setMaterialContent {
+            Container {
+                OnChildPositioned(onPositioned = { coords ->
+                    appBarCoords = coords
+                }) {
+                    BottomAppBar(
+                        contextualActions = createImageList(1),
+                        action = {
+                            OnChildPositioned(onPositioned = { coords ->
+                                actionCoords = coords
+                            }) { it() }
+                        }
+                    )
+                }
+            }
+        }
+
+        withDensity(composeTestRule.density) {
+            // Action should still be placed at the end, even though there is no navigation icon
+            val actionPositionX = actionCoords!!.localToGlobal(PxPosition.Origin).x
+            val actionExpectedPositionX = appBarCoords!!.size.width.round().toPx() -
+                    16.dp.toIntPx().toPx() - 24.dp.toIntPx().toPx()
+            Truth.assertThat(actionPositionX).isEqualTo(actionExpectedPositionX)
+        }
+    }
+
+    @Test
     fun bottomAppBar_noFab_positioning() {
         var appBarCoords: LayoutCoordinates? = null
         var navigationIconCoords: LayoutCoordinates? = null
diff --git a/ui/ui-material/src/androidTest/java/androidx/ui/material/TabTest.kt b/ui/ui-material/src/androidTest/java/androidx/ui/material/TabTest.kt
index d6906c6..b4b1c5c 100644
--- a/ui/ui-material/src/androidTest/java/androidx/ui/material/TabTest.kt
+++ b/ui/ui-material/src/androidTest/java/androidx/ui/material/TabTest.kt
@@ -15,9 +15,20 @@
  */
 package androidx.ui.material
 
+import androidx.compose.Composable
 import androidx.compose.composer
+import androidx.compose.state
+import androidx.compose.unaryPlus
 import androidx.test.filters.LargeTest
+import androidx.ui.core.LayoutCoordinates
+import androidx.ui.core.OnChildPositioned
+import androidx.ui.core.PxPosition
 import androidx.ui.core.dp
+import androidx.ui.core.toPx
+import androidx.ui.core.withDensity
+import androidx.ui.foundation.ColoredRect
+import androidx.ui.graphics.Color
+import androidx.ui.layout.Alignment
 import androidx.ui.layout.Container
 import androidx.ui.material.samples.TextTabs
 import androidx.ui.material.surface.Surface
@@ -29,6 +40,7 @@
 import androidx.ui.test.createComposeRule
 import androidx.ui.test.doClick
 import androidx.ui.test.findAll
+import com.google.common.truth.Truth
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -91,6 +103,74 @@
     }
 
     @Test
+    fun tabRow_indicatorPosition() {
+        val indicatorHeight = 1.dp
+        var tabRowCoords: LayoutCoordinates? = null
+        var indicatorCoords: LayoutCoordinates? = null
+
+        composeTestRule
+            .setMaterialContent {
+                val state = +state { 0 }
+                val titles = listOf("TAB 1", "TAB 2")
+
+                val indicatorContainer = @Composable { tabPositions: List<TabRow.TabPosition> ->
+                    TabRow.IndicatorContainer(tabPositions, state.value) {
+                        OnChildPositioned({ indicatorCoords = it }) {
+                            ColoredRect(Color.Red, height = indicatorHeight)
+                        }
+                    }
+                }
+
+                Container(alignment = Alignment.TopCenter) {
+                    OnChildPositioned({ tabRowCoords = it }) {
+                        TabRow(
+                            items = titles,
+                            selectedIndex = state.value,
+                            indicatorContainer = indicatorContainer
+                        ) { index, text ->
+                            Tab(text = text, selected = state.value == index) {
+                                state.value = index
+                            }
+                        }
+                    }
+                }
+            }
+
+        val tabRowWidth = tabRowCoords!!.size.width
+        val tabRowHeight = tabRowCoords!!.size.height
+
+        // Indicator should be placed in the bottom left of the first tab
+        withDensity(composeTestRule.density) {
+            val indicatorPositionX = indicatorCoords!!.localToGlobal(PxPosition.Origin).x
+            val expectedPositionX = 0.dp.toPx()
+            Truth.assertThat(indicatorPositionX).isEqualTo(expectedPositionX)
+
+            val indicatorPositionY = indicatorCoords!!.localToGlobal(PxPosition.Origin).y
+            val expectedPositionY = tabRowHeight - indicatorHeight.toIntPx().toPx()
+            Truth.assertThat(indicatorPositionY).isEqualTo(expectedPositionY)
+        }
+
+        // Click the second tab
+        findAll { isInMutuallyExclusiveGroup }[1].doClick()
+
+        // TODO: we aren't correctly waiting for recompositions after clicking, so we need to wait
+        // again
+        findAll { isInMutuallyExclusiveGroup }
+
+        // Indicator should now be placed in the bottom left of the second tab, so its x coordinate
+        // should be in the middle of the TabRow
+        withDensity(composeTestRule.density) {
+            val indicatorPositionX = indicatorCoords!!.localToGlobal(PxPosition.Origin).x
+            val expectedPositionX = tabRowWidth / 2
+            Truth.assertThat(indicatorPositionX).isEqualTo(expectedPositionX)
+
+            val indicatorPositionY = indicatorCoords!!.localToGlobal(PxPosition.Origin).y
+            val expectedPositionY = tabRowHeight - indicatorHeight.toIntPx().toPx()
+            Truth.assertThat(indicatorPositionY).isEqualTo(expectedPositionY)
+        }
+    }
+
+    @Test
     fun tabRow_initialTabSelected() {
         composeTestRule
             .setMaterialContent {
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt b/ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt
index c3c4705..84652bd 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt
@@ -40,6 +40,7 @@
 import androidx.ui.layout.DpConstraints
 import androidx.ui.layout.EdgeInsets
 import androidx.ui.layout.Stack
+import androidx.ui.layout.Wrap
 import androidx.ui.material.BottomAppBar.FabPosition
 import androidx.ui.material.BottomAppBar.FabPosition.Center
 import androidx.ui.material.BottomAppBar.FabPosition.End
@@ -51,7 +52,7 @@
  * A TopAppBar displays information and actions relating to the current screen and is placed at the
  * top of the screen.
  *
- * @sample androidx.ui.material.samples.SimpleTopAppBar
+ * @sample androidx.ui.material.samples.SimpleTopAppBarNavIcon
  *
  * @param title The title to be displayed in the center of the TopAppBar
  * @param color An optional color for the TopAppBar. By default [MaterialColors.primary] will be
@@ -64,11 +65,13 @@
  * called for items in [contextualActions] up to the maximum number of icons that can be displayed.
  * @param T the type of item in [contextualActions]
  */
+// TODO: b/137311217 - type inference for nullable lambdas currently doesn't work
+@Suppress("USELESS_CAST")
 @Composable
 fun <T> TopAppBar(
     title: @Composable() () -> Unit = {},
     color: Color = +themeColor { primary },
-    navigationIcon: @Composable() () -> Unit = {},
+    navigationIcon: @Composable() (() -> Unit)? = null as @Composable() (() -> Unit)?,
     contextualActions: List<T>? = null,
     action: @Composable() (T) -> Unit = {}
     // TODO: support overflow menu here with the remainder of the list
@@ -76,7 +79,12 @@
     BaseTopAppBar(
         color = color,
         startContent = navigationIcon,
-        title = title,
+        title = {
+            // Text color comes from the underlying Surface
+            CurrentTextStyleProvider(value = +themeTextStyle { h6 }) {
+                title()
+            }
+        },
         endContent = {
             if (contextualActions != null) {
                 AppBarActions(MaxIconsInTopAppBar, contextualActions, action)
@@ -88,27 +96,27 @@
 @Composable
 private fun BaseTopAppBar(
     color: Color = +themeColor { primary },
-    startContent: @Composable() () -> Unit,
+    startContent: @Composable() (() -> Unit)?,
     title: @Composable() () -> Unit,
     endContent: @Composable() () -> Unit
 ) {
     BaseAppBar(color) {
         FlexRow(mainAxisAlignment = MainAxisAlignment.SpaceBetween) {
-            inflexible {
-                // TODO: what should the spacing be when there is no icon provided here?
-                startContent()
-                // TODO: this accidentally works now because expanded fills up the space, but this
-                // is actually adding another item for flex row to measure, meaning that when the
-                // expanded block is empty, we are emitting an empty spacer in the middle of the row
-                WidthSpacer(width = 32.dp)
-            }
-            expanded(1f) {
-                CurrentTextStyleProvider(value = +themeTextStyle { h6 }) {
-                    title()
+            // We only want to reserve space here if we have some start content
+            if (startContent != null) {
+                inflexible {
+                    Container(width = AppBarTitleStartPadding, alignment = Alignment.CenterLeft) {
+                        startContent()
+                    }
                 }
             }
+            expanded(1f) {
+                title()
+            }
             inflexible {
-                endContent()
+                Wrap {
+                    endContent()
+                }
             }
         }
     }
@@ -211,6 +219,7 @@
         BaseBottomAppBar(
             color = color,
             startContent = navigationIconComposable,
+            fab = null as @Composable() (() -> Unit)?,
             endContent = actions(MaxIconsInBottomAppBarNoFab)
         )
         return
@@ -220,7 +229,8 @@
         End -> BaseBottomAppBar(
             color = color,
             startContent = actions(MaxIconsInBottomAppBarEndFab),
-            fab = { Align(Alignment.CenterRight) { floatingActionButton() } }
+            fab = { Align(Alignment.CenterRight) { floatingActionButton() } },
+            endContent = {}
         )
         // TODO: support CenterCut
         else -> BaseBottomAppBar(
@@ -232,14 +242,12 @@
     }
 }
 
-// TODO: b/137311217 - type inference for nullable lambdas currently doesn't work
-@Suppress("USELESS_CAST")
 @Composable
 private fun BaseBottomAppBar(
     color: Color = +themeColor { primary },
-    startContent: @Composable() () -> Unit = {},
-    fab: (@Composable() () -> Unit)? = null as @Composable() (() -> Unit)?,
-    endContent: @Composable() () -> Unit = {}
+    startContent: @Composable() () -> Unit,
+    fab: @Composable() (() -> Unit)?,
+    endContent: @Composable() () -> Unit
 ) {
     val appBar = @Composable { BaseBottomAppBarWithoutFab(color, startContent, endContent) }
     if (fab == null) {
@@ -277,15 +285,17 @@
     BaseAppBar(color) {
         FlexRow(mainAxisAlignment = MainAxisAlignment.SpaceBetween) {
             inflexible {
-                startContent()
-                // TODO: if startContent() doesn't have any layout, then the endContent won't be
-                // placed at the end, so we need to trick it with a spacer
-                // TODO: if startContent() does have layout, this currently inserts an extra
-                // useless spacer which is placed evenly in the middle of the AppBar - this may
-                // cause rounding alignment issues.
-                WidthSpacer(width = 1.dp)
+                // Using a wrap so that even if startContent() is empty, we will still force
+                // end content to be placed at the end of the row.
+                Wrap {
+                    startContent()
+                }
             }
-            inflexible { endContent() }
+            inflexible {
+                Wrap {
+                    endContent()
+                }
+            }
         }
     }
 }
@@ -351,12 +361,10 @@
  */
 @Composable
 fun AppBarIcon(icon: Image, onClick: () -> Unit) {
-    Ripple(bounded = false) {
-        Clickable(onClick = onClick) {
-            Center {
-                Container(width = ActionIconDiameter, height = ActionIconDiameter) {
-                    SimpleImage(icon)
-                }
+    Container(width = ActionIconDiameter, height = ActionIconDiameter) {
+        Ripple(bounded = false) {
+            Clickable(onClick = onClick) {
+                SimpleImage(icon)
             }
         }
     }
@@ -367,6 +375,7 @@
 private val AppBarHeight = 56.dp
 private val BottomAppBarHeightWithFab = 84.dp
 private val AppBarPadding = 16.dp
+private val AppBarTitleStartPadding = 72.dp - AppBarPadding
 
 private const val MaxIconsInTopAppBar = 2
 private const val MaxIconsInBottomAppBarCenterFab = 2
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/Checkbox.kt b/ui/ui-material/src/main/java/androidx/ui/material/Checkbox.kt
index 51c1c4b..5437f266 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/Checkbox.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/Checkbox.kt
@@ -137,7 +137,7 @@
 
 @Composable
 private fun DrawCheckbox(value: ToggleableState, activeColor: Color) {
-    val unselectedColor = (+themeColor { onSurface }).copy(alpha = UncheckedBoxOppacity)
+    val unselectedColor = (+themeColor { onSurface }).copy(alpha = UncheckedBoxOpacity)
     val definition = +memo(activeColor, unselectedColor) {
         generateTransitionDefinition(activeColor, unselectedColor)
     }
@@ -313,5 +313,5 @@
 private val StrokeWidth = 2.dp
 private val RadiusSize = 2.dp
 
-private val UncheckedBoxOppacity = 0.6f
-private val CheckStrokeDefaultColor = Color(0xFFFFFFFF.toInt())
\ No newline at end of file
+private val UncheckedBoxOpacity = 0.6f
+private val CheckStrokeDefaultColor = Color.White
\ No newline at end of file
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 a6c1f37..296208b 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
@@ -123,11 +123,11 @@
     /**
      * The background color appears behind scrollable content.
      */
-    val background: Color = Color(0xFFFFFFFF.toInt()),
+    val background: Color = Color.White,
     /**
      * The surface color is used on surfaces of components, such as cards, sheets and menus.
      */
-    val surface: Color = Color(0xFFFFFFFF.toInt()),
+    val surface: Color = Color.White,
     /**
      * The error color is used to indicate error within components, such as text fields.
      */
@@ -135,23 +135,23 @@
     /**
      * Color used for text and icons displayed on top of the primary color.
      */
-    val onPrimary: Color = Color(0xFFFFFFFF.toInt()),
+    val onPrimary: Color = Color.White,
     /**
      * Color used for text and icons displayed on top of the secondary color.
      */
-    val onSecondary: Color = Color(0xFF000000.toInt()),
+    val onSecondary: Color = Color.Black,
     /**
      * Color used for text and icons displayed on top of the background color.
      */
-    val onBackground: Color = Color(0xFF000000.toInt()),
+    val onBackground: Color = Color.Black,
     /**
      * Color used for text and icons displayed on top of the surface color.
      */
-    val onSurface: Color = Color(0xFF000000.toInt()),
+    val onSurface: Color = Color.Black,
     /**
      * Color used for text and icons displayed on top of the error color.
      */
-    val onError: Color = Color(0xFFFFFFFF.toInt())
+    val onError: Color = Color.White
 )
 
 /**
@@ -233,7 +233,7 @@
                 ) { // light bg
                     materialColors.primary.copy(alpha = 0.12f)
                 } else { // dark bg
-                    Color(0xFFFFFFFF.toInt()).copy(alpha = 0.24f)
+                    Color.White.copy(alpha = 0.24f)
                 }
             }
         )
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt b/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt
index 7f5c452..bd85562 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/Tab.kt
@@ -31,7 +31,6 @@
 import androidx.ui.core.Dp
 import androidx.ui.core.WithConstraints
 import androidx.ui.core.hasBoundedWidth
-import androidx.ui.core.px
 import androidx.ui.core.withDensity
 import androidx.ui.foundation.ColoredRect
 import androidx.ui.foundation.SimpleImage
@@ -44,6 +43,7 @@
 import androidx.ui.layout.MainAxisAlignment
 import androidx.ui.layout.Padding
 import androidx.ui.layout.Stack
+import androidx.ui.material.TabRow.TabPosition
 import androidx.ui.material.ripple.Ripple
 import androidx.ui.material.surface.Surface
 import androidx.ui.painting.Image
@@ -58,26 +58,67 @@
  *
  * You can also provide your own custom tab, such as:
  *
- * @sample androidx.ui.material.samples.CustomTabs
+ * @sample androidx.ui.material.samples.FancyTabs
  *
  * Where the custom tab itself could look like:
  *
  * @sample androidx.ui.material.samples.FancyTab
  *
+ * As well as customizing the tab, you can also provide a custom [indicatorContainer], to customize
+ * the indicator displayed for a tab. [indicatorContainer] is responsible for positioning an
+ * indicator and for animating its position when [selectedIndex] changes.
+ *
+ * For example, given an indicator that draws a rounded rectangle near the edges of the [Tab]:
+ *
+ * @sample androidx.ui.material.samples.FancyIndicator
+ *
+ * We can reuse [TabRow.IndicatorContainer] and just provide this indicator, as we aren't changing
+ * the transition:
+ *
+ * @sample androidx.ui.material.samples.FancyIndicatorTabs
+ *
+ * You may also want to provide a custom transition, to allow you to dynamically change the
+ * appearance of the indicator as it animates between tabs, such as changing its color or size.
+ * [indicatorContainer] is stacked on top of the entire TabRow, so you just need to provide a custom
+ * container that animates the offset of the indicator from the start of the TabRow and place your
+ * custom indicator inside of it. For example, take the following custom container that animates
+ * position of the indicator, the color of the indicator, and also adds a physics based 'spring'
+ * effect to the indicator in the direction of motion:
+ *
+ * @sample androidx.ui.material.samples.FancyIndicatorContainer
+ *
+ * This container will fill up the entire width of the TabRow, and when a new tab is selected,
+ * the transition will be called with a new value for [selectedIndex], which will animate the
+ * indicator to the position of the new tab.
+ *
+ * We can use this custom container similarly to before:
+ *
+ * @sample androidx.ui.material.samples.FancyIndicatorContainerTabs
+ *
  * @param T the type of the item provided that will map to a [Tab]
  * @param items the list containing the items used to build this TabRow
  * @param selectedIndex the index of the currently selected tab
+ * @param indicatorContainer the container responsible for positioning and animating the position of
+ * the indicator between tabs. By default this will be [TabRow.IndicatorContainer], which animates a
+ * [TabRow.Indicator] between tabs.
  * @param tab the [Tab] to be emitted for the given index and element of type [T] in [items]
  *
  * @throws IllegalStateException when TabRow's parent has [Px.Infinity] width
  */
+
+// TODO: b/137311217 - type inference for nullable lambdas currently doesn't work
+@Suppress("USELESS_CAST")
 @Composable
 fun <T> TabRow(
     items: List<T>,
     selectedIndex: Int,
+    indicatorContainer: @Composable() (tabPositions: List<TabPosition>) -> Unit = { tabPositions ->
+        TabRow.IndicatorContainer(tabPositions, selectedIndex) {
+            TabRow.Indicator()
+        }
+    },
     tab: @Composable() (Int, T) -> Unit
 ) {
-    val count = items.size
     Surface(color = +themeColor { primary }) {
         WithConstraints { constraints ->
             // TODO : think about Infinite max bounds case
@@ -85,84 +126,133 @@
             val totalWidth = +withDensity {
                 constraints.maxWidth.toDp()
             }
-            val indicatorWidth = totalWidth / count
-            TabIndicatorTransition(count, indicatorWidth, selectedIndex) { indicatorPosition ->
-                Stack {
-                    aligned(Alignment.Center) {
-                        FlexRow {
-                            items.forEachIndexed { index, item ->
-                                expanded(1f) {
-                                    tab(index, item)
-                                }
+            val height = +withDensity {
+                constraints.maxHeight.toDp()
+            }
+
+            val tabCount = items.size
+            val tabWidth = totalWidth / tabCount
+
+            val tabPositions = +memo(tabCount, tabWidth) {
+                (0 until tabCount).map { index ->
+                    val xOffset = (tabWidth * index)
+                    TabPosition(xOffset = xOffset, width = tabWidth, height = height)
+                }
+            }
+
+            Stack {
+                aligned(Alignment.Center) {
+                    FlexRow {
+                        items.forEachIndexed { index, item ->
+                            expanded(1f) {
+                                tab(index, item)
                             }
                         }
                     }
-                    aligned(Alignment.BottomCenter) {
-                        TabRowDivider()
-                    }
-                    positioned(leftInset = indicatorPosition, bottomInset = 0.dp) {
-                        TabIndicator(indicatorWidth)
-                    }
+                }
+                aligned(Alignment.BottomCenter) {
+                    TabRow.Divider()
+                }
+                positioned(0.dp, 0.dp, 0.dp, 0.dp) {
+                    indicatorContainer(tabPositions)
                 }
             }
         }
     }
 }
 
-private val IndicatorPosition = DpPropKey()
+object TabRow {
+    private val IndicatorOffset = DpPropKey()
 
-/**
- * [Transition] defining how the indicator position animates between tabs, when a new tab is
- * selected.
- */
-@Composable
-private fun TabIndicatorTransition(
-    tabCount: Int,
-    indicatorWidth: Dp,
-    selectedIndex: Int,
-    children: @Composable() (indicatorPosition: Dp) -> Unit
-) {
-    val transitionDefinition = +memo(tabCount, indicatorWidth) {
-        transitionDefinition {
-            // TODO: currently the first state set is the 'default' state, so we want to define the
-            // state that is initially selected first, so we don't have any initial animations
-            // when this is supported by transitionDefinition, we should fix this to just set a
-            // default or similar
-            state(selectedIndex) {
-                this[IndicatorPosition] = indicatorWidth * selectedIndex
-            }
-            (0 until tabCount).minus(selectedIndex).forEach { tabIndex ->
-                state(tabIndex) {
-                    this[IndicatorPosition] = indicatorWidth * tabIndex
-                }
-            }
+    /**
+     * Data class that contains information about a tab's position on screen
+     *
+     * @param xOffset how far from the start of the [TabRow] this tab is positioned
+     * @param width the width of this tab
+     * @param height the height of this tab
+     */
+    data class TabPosition(val xOffset: Dp, val width: Dp, val height: Dp)
 
-            transition {
-                IndicatorPosition using tween {
-                    duration = 250
-                    easing = FastOutSlowInEasing
+    /**
+     * Positions and animates the given [indicator] between tabs when [selectedIndex] changes.
+     */
+    @Composable
+    fun IndicatorContainer(
+        tabPositions: List<TabPosition>,
+        selectedIndex: Int,
+        indicator: @Composable() () -> Unit
+    ) {
+        // TODO: should we animate the width of the indicator as it moves between tabs of different
+        // sizes inside a scrolling container?
+        val currentTabWidth = tabPositions[selectedIndex].width
+
+        Container(expanded = true, alignment = Alignment.BottomLeft) {
+            IndicatorTransition(tabPositions, selectedIndex) { indicatorOffset ->
+                Padding(left = indicatorOffset) {
+                    Container(width = currentTabWidth) {
+                        indicator()
+                    }
                 }
             }
         }
     }
-    Transition(transitionDefinition, selectedIndex) { state ->
-        children(state[IndicatorPosition])
+
+    /**
+     * Default indicator, which will be positioned at the bottom of the tab, on top of the divider.
+     *
+     * This is used as the default indicator inside [TabRow].
+     */
+    @Composable
+    fun Indicator() {
+        ColoredRect(color = +themeColor { onPrimary }, height = IndicatorHeight)
     }
-}
 
-@Composable
-private fun TabIndicator(width: Dp) {
-    ColoredRect(
-        color = +themeColor { onPrimary },
-        height = IndicatorHeight,
-        width = width
-    )
-}
+    /**
+     * [Transition] that animates the indicator offset between a given list of [TabPosition]s.
+     */
+    @Composable
+    internal fun IndicatorTransition(
+        tabPositions: List<TabPosition>,
+        selectedIndex: Int,
+        children: @Composable() (indicatorOffset: Dp) -> Unit
+    ) {
+        val transitionDefinition = +memo(tabPositions) {
+            transitionDefinition {
+                // TODO: currently the first state set is the 'default' state, so we want to define the
+                // state that is initially selected first, so we don't have any initial animations
+                // when this is supported by transitionDefinition, we should fix this to just set a
+                // default or similar
+                state(selectedIndex) {
+                    this[IndicatorOffset] = tabPositions[selectedIndex].xOffset
+                }
 
-@Composable
-private fun TabRowDivider() {
-    val onPrimary = +themeColor { onPrimary }
-    Divider(color = (onPrimary.copy(alpha = DividerOpacity)))
+                tabPositions.forEachIndexed { index, position ->
+                    if (index != selectedIndex) {
+                        state(index) {
+                            this[IndicatorOffset] = position.xOffset
+                        }
+                    }
+                }
+
+                transition {
+                    IndicatorOffset using tween {
+                        duration = 250
+                        easing = FastOutSlowInEasing
+                    }
+                }
+            }
+        }
+
+        Transition(transitionDefinition, selectedIndex) { state ->
+            children(state[IndicatorOffset])
+        }
+    }
+
+    @Composable
+    internal fun Divider() {
+        val onPrimary = +themeColor { onPrimary }
+        Divider(color = (onPrimary.copy(alpha = DividerOpacity)))
+    }
 }
 
 /**
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 6f61e77..139a554 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
@@ -108,8 +108,7 @@
         )
         val color = theme.colorCallback.invoke(rippleSurface.backgroundColor)
         val onAnimationFinished = { effect: RippleEffect ->
-            val contains = effects.remove(effect)
-            require(contains)
+            effects.remove(effect)
             if (currentEffect == effect) {
                 currentEffect = null
             }
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 77a835f..0989de0 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
@@ -163,6 +163,14 @@
             KeyboardType.Email ->
                 outInfo.inputType =
                     InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
+            KeyboardType.Password -> {
+                outInfo.inputType =
+                    InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_PASSWORD
+            }
+            KeyboardType.NumberPassword -> {
+                outInfo.inputType =
+                        InputType.TYPE_CLASS_NUMBER or EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD
+            }
             else -> throw IllegalArgumentException("Unknown KeyboardType: $keyboardType")
         }
         outInfo.imeOptions =
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 81f82ab..575460d 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
@@ -157,6 +157,42 @@
     }
 
     @Test
+    fun test_fill_editor_info_password() {
+        textInputService.startInput(
+            EditorState(""),
+            KeyboardType.Password,
+            ImeAction.Unspecified,
+            onEditCommand = {},
+            onImeActionPerformed = {})
+
+        EditorInfo().let { info ->
+            textInputService.createInputConnection(info)
+            assertTrue((InputType.TYPE_CLASS_TEXT and info.inputType) != 0)
+            assertTrue((InputType.TYPE_TEXT_VARIATION_PASSWORD and info.inputType) != 0)
+            assertTrue((EditorInfo.IME_MASK_ACTION and info.imeOptions)
+                    == EditorInfo.IME_ACTION_UNSPECIFIED)
+        }
+    }
+
+    @Test
+    fun test_fill_editor_info_number_password() {
+        textInputService.startInput(
+            EditorState(""),
+            KeyboardType.NumberPassword,
+            ImeAction.Unspecified,
+            onEditCommand = {},
+            onImeActionPerformed = {})
+
+        EditorInfo().let { info ->
+            textInputService.createInputConnection(info)
+            assertTrue((InputType.TYPE_CLASS_NUMBER and info.inputType) != 0)
+            assertTrue((InputType.TYPE_NUMBER_VARIATION_PASSWORD and info.inputType) != 0)
+            assertTrue((EditorInfo.IME_MASK_ACTION and info.imeOptions)
+                    == EditorInfo.IME_ACTION_UNSPECIFIED)
+        }
+    }
+
+    @Test
     fun test_fill_editor_info_action_none() {
         textInputService.startInput(
             EditorState(""),
diff --git a/ui/ui-text/api/1.0.0-alpha01.txt b/ui/ui-text/api/1.0.0-alpha01.txt
index 0aa0326..59a09a0 100644
--- a/ui/ui-text/api/1.0.0-alpha01.txt
+++ b/ui/ui-text/api/1.0.0-alpha01.txt
@@ -26,6 +26,8 @@
     enum_constant public static final androidx.ui.input.KeyboardType Ascii;
     enum_constant public static final androidx.ui.input.KeyboardType Email;
     enum_constant public static final androidx.ui.input.KeyboardType Number;
+    enum_constant public static final androidx.ui.input.KeyboardType NumberPassword;
+    enum_constant public static final androidx.ui.input.KeyboardType Password;
     enum_constant public static final androidx.ui.input.KeyboardType Phone;
     enum_constant public static final androidx.ui.input.KeyboardType Text;
     enum_constant public static final androidx.ui.input.KeyboardType Uri;
@@ -76,7 +78,6 @@
 
   public interface Paragraph {
     method public float getBaseline();
-    method public androidx.ui.engine.geometry.Rect getBoundingBox(int offset);
     method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
     method public boolean getDidExceedMaxLines();
     method public float getHeight();
@@ -87,12 +88,12 @@
     method public float getLineWidth(int lineIndex);
     method public float getMaxIntrinsicWidth();
     method public float getMinIntrinsicWidth();
+    method public int getOffsetForPosition(androidx.ui.core.PxPosition position);
     method public androidx.ui.painting.Path getPathForRange(int start, int end);
-    method public int getPositionForOffset(androidx.ui.engine.geometry.Offset offset);
     method public float getWidth();
     method public androidx.ui.text.TextRange getWordBoundary(int offset);
     method public void layout(androidx.ui.text.ParagraphConstraints constraints);
-    method public void paint(androidx.ui.painting.Canvas canvas, float x, float y);
+    method public void paint(androidx.ui.painting.Canvas canvas);
     property public abstract float baseline;
     property public abstract boolean didExceedMaxLines;
     property public abstract float height;
@@ -137,9 +138,9 @@
     method public float getMaxIntrinsicWidth();
     method public Integer? getMaxLines();
     method public float getMinIntrinsicWidth();
+    method public int getOffsetForPosition(androidx.ui.core.PxPosition position);
     method public androidx.ui.text.style.TextOverflow getOverflow();
     method public androidx.ui.text.ParagraphStyle? getParagraphStyle();
-    method public int getPositionForOffset(androidx.ui.engine.geometry.Offset offset);
     method public float getPreferredLineHeight();
     method public androidx.ui.text.font.Font.ResourceLoader getResourceLoader();
     method public androidx.ui.engine.geometry.Size getSize();
@@ -147,10 +148,10 @@
     method public androidx.ui.text.TextStyle? getStyle();
     method public androidx.ui.text.AnnotatedString? getText();
     method public float getWidth();
-    method public androidx.ui.text.TextRange getWordBoundary(int position);
+    method public androidx.ui.text.TextRange getWordBoundary(int offset);
     method public void layout(androidx.ui.core.Constraints constraints);
-    method public void paint(androidx.ui.painting.Canvas canvas, androidx.ui.engine.geometry.Offset offset);
-    method public void paintBackground(int start, int end, androidx.ui.graphics.Color color, androidx.ui.painting.Canvas canvas, androidx.ui.engine.geometry.Offset offset);
+    method public void paint(androidx.ui.painting.Canvas canvas);
+    method public void paintBackground(int start, int end, androidx.ui.graphics.Color color, androidx.ui.painting.Canvas canvas);
     method public void paintCursor(int offset, androidx.ui.painting.Canvas canvas);
     method public void setText(androidx.ui.text.AnnotatedString? value);
     property public final boolean didExceedMaxLines;
diff --git a/ui/ui-text/api/current.txt b/ui/ui-text/api/current.txt
index 0aa0326..59a09a0 100644
--- a/ui/ui-text/api/current.txt
+++ b/ui/ui-text/api/current.txt
@@ -26,6 +26,8 @@
     enum_constant public static final androidx.ui.input.KeyboardType Ascii;
     enum_constant public static final androidx.ui.input.KeyboardType Email;
     enum_constant public static final androidx.ui.input.KeyboardType Number;
+    enum_constant public static final androidx.ui.input.KeyboardType NumberPassword;
+    enum_constant public static final androidx.ui.input.KeyboardType Password;
     enum_constant public static final androidx.ui.input.KeyboardType Phone;
     enum_constant public static final androidx.ui.input.KeyboardType Text;
     enum_constant public static final androidx.ui.input.KeyboardType Uri;
@@ -76,7 +78,6 @@
 
   public interface Paragraph {
     method public float getBaseline();
-    method public androidx.ui.engine.geometry.Rect getBoundingBox(int offset);
     method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
     method public boolean getDidExceedMaxLines();
     method public float getHeight();
@@ -87,12 +88,12 @@
     method public float getLineWidth(int lineIndex);
     method public float getMaxIntrinsicWidth();
     method public float getMinIntrinsicWidth();
+    method public int getOffsetForPosition(androidx.ui.core.PxPosition position);
     method public androidx.ui.painting.Path getPathForRange(int start, int end);
-    method public int getPositionForOffset(androidx.ui.engine.geometry.Offset offset);
     method public float getWidth();
     method public androidx.ui.text.TextRange getWordBoundary(int offset);
     method public void layout(androidx.ui.text.ParagraphConstraints constraints);
-    method public void paint(androidx.ui.painting.Canvas canvas, float x, float y);
+    method public void paint(androidx.ui.painting.Canvas canvas);
     property public abstract float baseline;
     property public abstract boolean didExceedMaxLines;
     property public abstract float height;
@@ -137,9 +138,9 @@
     method public float getMaxIntrinsicWidth();
     method public Integer? getMaxLines();
     method public float getMinIntrinsicWidth();
+    method public int getOffsetForPosition(androidx.ui.core.PxPosition position);
     method public androidx.ui.text.style.TextOverflow getOverflow();
     method public androidx.ui.text.ParagraphStyle? getParagraphStyle();
-    method public int getPositionForOffset(androidx.ui.engine.geometry.Offset offset);
     method public float getPreferredLineHeight();
     method public androidx.ui.text.font.Font.ResourceLoader getResourceLoader();
     method public androidx.ui.engine.geometry.Size getSize();
@@ -147,10 +148,10 @@
     method public androidx.ui.text.TextStyle? getStyle();
     method public androidx.ui.text.AnnotatedString? getText();
     method public float getWidth();
-    method public androidx.ui.text.TextRange getWordBoundary(int position);
+    method public androidx.ui.text.TextRange getWordBoundary(int offset);
     method public void layout(androidx.ui.core.Constraints constraints);
-    method public void paint(androidx.ui.painting.Canvas canvas, androidx.ui.engine.geometry.Offset offset);
-    method public void paintBackground(int start, int end, androidx.ui.graphics.Color color, androidx.ui.painting.Canvas canvas, androidx.ui.engine.geometry.Offset offset);
+    method public void paint(androidx.ui.painting.Canvas canvas);
+    method public void paintBackground(int start, int end, androidx.ui.graphics.Color color, androidx.ui.painting.Canvas canvas);
     method public void paintCursor(int offset, androidx.ui.painting.Canvas canvas);
     method public void setText(androidx.ui.text.AnnotatedString? value);
     property public final boolean didExceedMaxLines;
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 e2f3b68..cb841ce 100644
--- a/ui/ui-text/api/restricted_1.0.0-alpha01.txt
+++ b/ui/ui-text/api/restricted_1.0.0-alpha01.txt
@@ -35,6 +35,8 @@
     enum_constant public static final androidx.ui.input.KeyboardType Ascii;
     enum_constant public static final androidx.ui.input.KeyboardType Email;
     enum_constant public static final androidx.ui.input.KeyboardType Number;
+    enum_constant public static final androidx.ui.input.KeyboardType NumberPassword;
+    enum_constant public static final androidx.ui.input.KeyboardType Password;
     enum_constant public static final androidx.ui.input.KeyboardType Phone;
     enum_constant public static final androidx.ui.input.KeyboardType Text;
     enum_constant public static final androidx.ui.input.KeyboardType Uri;
@@ -90,7 +92,6 @@
 
   public interface Paragraph {
     method public float getBaseline();
-    method public androidx.ui.engine.geometry.Rect getBoundingBox(int offset);
     method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
     method public boolean getDidExceedMaxLines();
     method public float getHeight();
@@ -101,12 +102,12 @@
     method public float getLineWidth(int lineIndex);
     method public float getMaxIntrinsicWidth();
     method public float getMinIntrinsicWidth();
+    method public int getOffsetForPosition(androidx.ui.core.PxPosition position);
     method public androidx.ui.painting.Path getPathForRange(int start, int end);
-    method public int getPositionForOffset(androidx.ui.engine.geometry.Offset offset);
     method public float getWidth();
     method public androidx.ui.text.TextRange getWordBoundary(int offset);
     method public void layout(androidx.ui.text.ParagraphConstraints constraints);
-    method public void paint(androidx.ui.painting.Canvas canvas, float x, float y);
+    method public void paint(androidx.ui.painting.Canvas canvas);
     property public abstract float baseline;
     property public abstract boolean didExceedMaxLines;
     property public abstract float height;
@@ -151,9 +152,9 @@
     method public float getMaxIntrinsicWidth();
     method public Integer? getMaxLines();
     method public float getMinIntrinsicWidth();
+    method public int getOffsetForPosition(androidx.ui.core.PxPosition position);
     method public androidx.ui.text.style.TextOverflow getOverflow();
     method public androidx.ui.text.ParagraphStyle? getParagraphStyle();
-    method public int getPositionForOffset(androidx.ui.engine.geometry.Offset offset);
     method public float getPreferredLineHeight();
     method public androidx.ui.text.font.Font.ResourceLoader getResourceLoader();
     method public androidx.ui.engine.geometry.Size getSize();
@@ -161,10 +162,10 @@
     method public androidx.ui.text.TextStyle? getStyle();
     method public androidx.ui.text.AnnotatedString? getText();
     method public float getWidth();
-    method public androidx.ui.text.TextRange getWordBoundary(int position);
+    method public androidx.ui.text.TextRange getWordBoundary(int offset);
     method public void layout(androidx.ui.core.Constraints constraints);
-    method public void paint(androidx.ui.painting.Canvas canvas, androidx.ui.engine.geometry.Offset offset);
-    method public void paintBackground(int start, int end, androidx.ui.graphics.Color color, androidx.ui.painting.Canvas canvas, androidx.ui.engine.geometry.Offset offset);
+    method public void paint(androidx.ui.painting.Canvas canvas);
+    method public void paintBackground(int start, int end, androidx.ui.graphics.Color color, androidx.ui.painting.Canvas canvas);
     method public void paintCursor(int offset, androidx.ui.painting.Canvas canvas);
     method public void setText(androidx.ui.text.AnnotatedString? value);
     property public final boolean didExceedMaxLines;
diff --git a/ui/ui-text/api/restricted_current.txt b/ui/ui-text/api/restricted_current.txt
index e2f3b68..cb841ce 100644
--- a/ui/ui-text/api/restricted_current.txt
+++ b/ui/ui-text/api/restricted_current.txt
@@ -35,6 +35,8 @@
     enum_constant public static final androidx.ui.input.KeyboardType Ascii;
     enum_constant public static final androidx.ui.input.KeyboardType Email;
     enum_constant public static final androidx.ui.input.KeyboardType Number;
+    enum_constant public static final androidx.ui.input.KeyboardType NumberPassword;
+    enum_constant public static final androidx.ui.input.KeyboardType Password;
     enum_constant public static final androidx.ui.input.KeyboardType Phone;
     enum_constant public static final androidx.ui.input.KeyboardType Text;
     enum_constant public static final androidx.ui.input.KeyboardType Uri;
@@ -90,7 +92,6 @@
 
   public interface Paragraph {
     method public float getBaseline();
-    method public androidx.ui.engine.geometry.Rect getBoundingBox(int offset);
     method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
     method public boolean getDidExceedMaxLines();
     method public float getHeight();
@@ -101,12 +102,12 @@
     method public float getLineWidth(int lineIndex);
     method public float getMaxIntrinsicWidth();
     method public float getMinIntrinsicWidth();
+    method public int getOffsetForPosition(androidx.ui.core.PxPosition position);
     method public androidx.ui.painting.Path getPathForRange(int start, int end);
-    method public int getPositionForOffset(androidx.ui.engine.geometry.Offset offset);
     method public float getWidth();
     method public androidx.ui.text.TextRange getWordBoundary(int offset);
     method public void layout(androidx.ui.text.ParagraphConstraints constraints);
-    method public void paint(androidx.ui.painting.Canvas canvas, float x, float y);
+    method public void paint(androidx.ui.painting.Canvas canvas);
     property public abstract float baseline;
     property public abstract boolean didExceedMaxLines;
     property public abstract float height;
@@ -151,9 +152,9 @@
     method public float getMaxIntrinsicWidth();
     method public Integer? getMaxLines();
     method public float getMinIntrinsicWidth();
+    method public int getOffsetForPosition(androidx.ui.core.PxPosition position);
     method public androidx.ui.text.style.TextOverflow getOverflow();
     method public androidx.ui.text.ParagraphStyle? getParagraphStyle();
-    method public int getPositionForOffset(androidx.ui.engine.geometry.Offset offset);
     method public float getPreferredLineHeight();
     method public androidx.ui.text.font.Font.ResourceLoader getResourceLoader();
     method public androidx.ui.engine.geometry.Size getSize();
@@ -161,10 +162,10 @@
     method public androidx.ui.text.TextStyle? getStyle();
     method public androidx.ui.text.AnnotatedString? getText();
     method public float getWidth();
-    method public androidx.ui.text.TextRange getWordBoundary(int position);
+    method public androidx.ui.text.TextRange getWordBoundary(int offset);
     method public void layout(androidx.ui.core.Constraints constraints);
-    method public void paint(androidx.ui.painting.Canvas canvas, androidx.ui.engine.geometry.Offset offset);
-    method public void paintBackground(int start, int end, androidx.ui.graphics.Color color, androidx.ui.painting.Canvas canvas, androidx.ui.engine.geometry.Offset offset);
+    method public void paint(androidx.ui.painting.Canvas canvas);
+    method public void paintBackground(int start, int end, androidx.ui.graphics.Color color, androidx.ui.painting.Canvas canvas);
     method public void paintCursor(int offset, androidx.ui.painting.Canvas canvas);
     method public void setText(androidx.ui.text.AnnotatedString? value);
     property public final boolean didExceedMaxLines;
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/AndroidManifest.xml b/ui/ui-text/integration-tests/text-demos/src/main/AndroidManifest.xml
index 77e5d1b..66c0d73 100644
--- a/ui/ui-text/integration-tests/text-demos/src/main/AndroidManifest.xml
+++ b/ui/ui-text/integration-tests/text-demos/src/main/AndroidManifest.xml
@@ -30,7 +30,16 @@
 
         <activity android:name=".CraneInputFieldActivity"
             android:configChanges="orientation|screenSize"
-            android:label="Text/Input Field Demo">
+            android:label="Text/Input Field/Input Field Demo">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="androidx.ui.demos.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".CraneVariousInputFieldActivity"
+                  android:configChanges="orientation|screenSize"
+                  android:label="Text/Input Field/Various Input Field Demo">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="androidx.ui.demos.SAMPLE_CODE" />
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 e377a1a..c71370d 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
@@ -36,7 +36,9 @@
     Pair(KeyboardType.Ascii, "Ascii"),
     Pair(KeyboardType.Number, "Number"),
     Pair(KeyboardType.Email, "Email"),
-    Pair(KeyboardType.Phone, "Phone")
+    Pair(KeyboardType.Phone, "Phone"),
+    Pair(KeyboardType.Password, "Password"),
+    Pair(KeyboardType.NumberPassword, "NumberPassword")
 )
 
 val IME_ACTIONS = listOf(
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
new file mode 100644
index 0000000..1f46d90
--- /dev/null
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneVariousInputField.kt
@@ -0,0 +1,243 @@
+/*
+ * 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.demos
+
+import androidx.compose.composer
+import androidx.compose.Composable
+import androidx.compose.state
+import androidx.compose.unaryPlus
+import androidx.ui.core.EditorStyle
+import androidx.ui.core.InputField
+import androidx.ui.core.OffsetMap
+import androidx.ui.core.PasswordVisualTransformation
+import androidx.ui.core.TransformedText
+import androidx.ui.core.VisualTransformation
+import androidx.ui.input.EditorState
+import androidx.ui.input.ImeAction
+import androidx.ui.input.KeyboardType
+import androidx.ui.layout.Column
+import androidx.ui.layout.CrossAxisAlignment
+import androidx.ui.layout.VerticalScroller
+import androidx.ui.text.AnnotatedString
+import androidx.ui.text.TextStyle
+import java.util.Locale
+
+/**
+ * The offset translater used for credit card input field.
+ *
+ * @see creditCardFilter
+ */
+private val creditCardOffsetTranslator = object : OffsetMap {
+    override fun originalToTransformed(offset: Int): Int {
+        if (offset <= 3) return offset
+        if (offset <= 7) return offset + 1
+        if (offset <= 11) return offset + 2
+        if (offset <= 16) return offset + 3
+        return 19
+    }
+
+    override fun transformedToOriginal(offset: Int): Int {
+        if (offset <= 4) return offset
+        if (offset <= 9) return offset - 1
+        if (offset <= 14) return offset - 2
+        if (offset <= 19) return offset - 3
+        return 16
+    }
+}
+
+/**
+ * The visual filter for credit card input field.
+ *
+ * This filter converts up to 16 digits to hyphen connected 4 digits string.
+ * For example, "1234567890123456" will be shown as "1234-5678-9012-3456".
+ */
+private val creditCardFilter = object : VisualTransformation {
+    override fun filter(text: AnnotatedString): TransformedText {
+        val trimmed = if (text.text.length >= 16) text.text.substring(0..15) else text.text
+        var out = ""
+        for (i in 0 until trimmed.length) {
+            out += trimmed[i]
+            if (i % 4 == 3 && i != 15) out += "-"
+        }
+        return TransformedText(AnnotatedString(out), creditCardOffsetTranslator)
+    }
+}
+
+/**
+ * The offset translator which works for all offset keep remains the same.
+ */
+private val identityTranslater = object : OffsetMap {
+    override fun originalToTransformed(offset: Int): Int = offset
+    override fun transformedToOriginal(offset: Int): Int = offset
+}
+
+/**
+ * The visual filter for capitalization.
+ *
+ * This filer converts ASCII characters to capital form.
+ */
+private class CapitalizeTransformation(val locale: Locale = Locale.US) : VisualTransformation {
+    override fun filter(text: AnnotatedString): TransformedText {
+        // TODO(nona): identityTranslater doesn't work for some locale, e.g. Turkish
+        return TransformedText(AnnotatedString(text.text.toUpperCase(locale)), identityTranslater)
+    }
+}
+
+/**
+ * The offset translator for phone number
+ *
+ * @see phoneNumberFilter
+ */
+private val phoneNumberOffsetTranslater = object : OffsetMap {
+    override fun originalToTransformed(offset: Int): Int {
+        return when (offset) {
+            0 -> 1
+            1 -> 2
+            2 -> 3
+            3 -> 6
+            4 -> 7
+            5 -> 8
+            6 -> 10
+            7 -> 11
+            8 -> 12
+            9 -> 13
+            else -> 14
+        }
+    }
+
+    override fun transformedToOriginal(offset: Int): Int {
+        return when (offset) {
+            0 -> 0
+            1 -> 0
+            2 -> 1
+            3 -> 2
+            4 -> 3
+            5 -> 3
+            6 -> 3
+            7 -> 4
+            8 -> 5
+            9 -> 6
+            10 -> 6
+            11 -> 7
+            12 -> 8
+            13 -> 9
+            else -> 10
+        }
+    }
+}
+
+/**
+ * The visual filter for phone number.
+ *
+ * This filter converts up to 10 digits to phone number form.
+ * For example, "1234567890" will be shown as "(123) 456-7890".
+ */
+private val phoneNumberFilter = object : VisualTransformation {
+    override fun filter(text: AnnotatedString): TransformedText {
+        val trimmed = if (text.text.length >= 10) text.text.substring(0..9) else text.text
+        val filled = trimmed + "_".repeat(10 - trimmed.length)
+        val res = "(" + filled.substring(0..2) + ") " + filled.substring(3..5) + "-" +
+                filled.substring(6..9)
+        return TransformedText(AnnotatedString(text = res), phoneNumberOffsetTranslater)
+    }
+}
+
+private val emailFilter = object : VisualTransformation {
+    override fun filter(text: AnnotatedString): TransformedText {
+        return if (text.text.indexOf("@") == -1) {
+            TransformedText(AnnotatedString(text = text.text + "@gmail.com"), identityTranslater)
+        } else {
+            TransformedText(text, identityTranslater)
+        }
+    }
+}
+
+@Composable
+fun VariousInputFieldDemo() {
+    VerticalScroller {
+        Column(crossAxisAlignment = CrossAxisAlignment.Start) {
+            TagLine(tag = "Capitalization")
+            VariousEditLine(
+                keyboardType = KeyboardType.Ascii,
+                onValueChange = { old, new ->
+                    if (new.text.any { !it.isLetterOrDigit() }) old else new
+                },
+                visualTransformation = CapitalizeTransformation()
+            )
+
+            TagLine(tag = "Capitalization (Turkish)")
+            VariousEditLine(
+                keyboardType = KeyboardType.Ascii,
+                onValueChange = { old, new ->
+                    if (new.text.any { !it.isLetterOrDigit() }) old else new
+                },
+                visualTransformation = CapitalizeTransformation(Locale.forLanguageTag("tr"))
+            )
+
+            TagLine(tag = "Password")
+            VariousEditLine(
+                keyboardType = KeyboardType.Password,
+                onValueChange = { old, new ->
+                    if (new.text.any { !it.isLetterOrDigit() }) old else new
+                },
+                visualTransformation = PasswordVisualTransformation()
+            )
+
+            TagLine(tag = "Phone Number")
+            VariousEditLine(
+                keyboardType = KeyboardType.Number,
+                onValueChange = { old, new ->
+                    if (new.text.length > 10 || new.text.any { !it.isDigit() }) old else new
+                },
+                visualTransformation = phoneNumberFilter
+            )
+
+            TagLine(tag = "Credit Card")
+            VariousEditLine(
+                keyboardType = KeyboardType.Number,
+                onValueChange = { old, new ->
+                    if (new.text.length > 16 || new.text.any { !it.isDigit() }) old else new
+                },
+                visualTransformation = creditCardFilter
+            )
+
+            TagLine(tag = "Email Suggestion")
+            VariousEditLine(
+                keyboardType = KeyboardType.Email,
+                visualTransformation = emailFilter
+            )
+        }
+    }
+}
+
+@Composable
+fun VariousEditLine(
+    keyboardType: KeyboardType = KeyboardType.Text,
+    imeAction: ImeAction = ImeAction.Unspecified,
+    onValueChange: (EditorState, EditorState) -> EditorState = { _, new -> new },
+    visualTransformation: VisualTransformation
+) {
+    val state = +state { EditorState() }
+    InputField(
+        value = state.value,
+        keyboardType = keyboardType,
+        imeAction = imeAction,
+        visualTransformation = visualTransformation,
+        onValueChange = { state.value = onValueChange(state.value, it) },
+        editorStyle = EditorStyle(textStyle = TextStyle(fontSize = fontSize8))
+    )
+}
diff --git a/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneVariousInputFieldActivity.kt b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneVariousInputFieldActivity.kt
new file mode 100644
index 0000000..0aae714
--- /dev/null
+++ b/ui/ui-text/integration-tests/text-demos/src/main/java/androidx/ui/text/demos/CraneVariousInputFieldActivity.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.demos
+
+import android.app.Activity
+import android.os.Bundle
+import androidx.compose.composer
+import androidx.ui.core.setContent
+
+class CraneVariousInputFieldActivity : Activity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContent { VariousInputFieldDemo() }
+    }
+}
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 b937a37..841eb1b 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
@@ -20,6 +20,7 @@
 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
@@ -208,7 +209,7 @@
     }
 
     @Test
-    fun getPositionForOffset_ltr() {
+    fun getOffsetForPosition_ltr() {
         withDensity(defaultDensity) {
             val text = "abc"
             val fontSize = 50.sp
@@ -218,11 +219,11 @@
             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 offset = Offset(i * fontSizeInPx + 1, fontSizeInPx / 2)
-                val position = paragraph.getPositionForOffset(offset)
+                val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx / 2).px)
+                val offset = paragraph.getOffsetForPosition(position)
                 assertThat(
-                    "position at index $i, offset $offset does not match",
-                    position,
+                    "offset at index $i, position $position does not match",
+                    offset,
                     equalTo(i)
                 )
             }
@@ -230,7 +231,7 @@
     }
 
     @Test
-    fun getPositionForOffset_rtl() {
+    fun getOffsetForPosition_rtl() {
         withDensity(defaultDensity) {
             val text = "\u05D0\u05D1\u05D2"
             val fontSize = 50.sp
@@ -241,11 +242,11 @@
 
             // test positions that are 1, fontSize+1, 2fontSize+1 which maps to chars .., 2, 1, 0
             for (i in 0..text.length) {
-                val offset = Offset(i * fontSizeInPx + 1, fontSizeInPx / 2)
-                val position = paragraph.getPositionForOffset(offset)
+                val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx / 2).px)
+                val offset = paragraph.getOffsetForPosition(position)
                 assertThat(
-                    "position at index $i, offset $offset does not match",
-                    position,
+                    "offset at index $i, position $position does not match",
+                    offset,
                     equalTo(text.length - i)
                 )
             }
@@ -253,7 +254,7 @@
     }
 
     @Test
-    fun getPositionForOffset_ltr_multiline() {
+    fun getOffsetForPosition_ltr_multiline() {
         withDensity(defaultDensity) {
             val firstLine = "abc"
             val secondLine = "def"
@@ -267,11 +268,11 @@
             // 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 offset = Offset(i * fontSizeInPx + 1, fontSizeInPx * 1.5f)
-                val position = paragraph.getPositionForOffset(offset)
+                val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx * 1.5f).px)
+                val offset = paragraph.getOffsetForPosition(position)
                 assertThat(
-                    "position at index $i, offset $offset, second line does not match",
-                    position,
+                    "offset at index $i, position $position, second line does not match",
+                    offset,
                     equalTo(i + firstLine.length)
                 )
             }
@@ -279,7 +280,7 @@
     }
 
     @Test
-    fun getPositionForOffset_rtl_multiline() {
+    fun getOffsetForPosition_rtl_multiline() {
         withDensity(defaultDensity) {
             val firstLine = "\u05D0\u05D1\u05D2"
             val secondLine = "\u05D3\u05D4\u05D5"
@@ -293,11 +294,11 @@
             // 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 offset = Offset(i * fontSizeInPx + 1, fontSizeInPx * 1.5f)
-                val position = paragraph.getPositionForOffset(offset)
+                val position = PxPosition((i * fontSizeInPx + 1).px, (fontSizeInPx * 1.5f).px)
+                val offset = paragraph.getOffsetForPosition(position)
                 assertThat(
-                    "position at index $i, offset $offset, second line does not match",
-                    position,
+                    "offset at index $i, position $position, second line does not match",
+                    offset,
                     equalTo(text.length - i)
                 )
             }
@@ -305,7 +306,7 @@
     }
 
     @Test
-    fun getPositionForOffset_ltr_width_outOfBounds() {
+    fun getOffsetForPosition_ltr_width_outOfBounds() {
         withDensity(defaultDensity) {
             val text = "abc"
             val fontSize = 50.sp
@@ -315,19 +316,19 @@
             paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
 
             // greater than width
-            var offset = Offset(fontSizeInPx * text.length * 2, fontSizeInPx / 2)
-            var position = paragraph.getPositionForOffset(offset)
-            assertThat(position, equalTo(text.length))
+            var position = PxPosition((fontSizeInPx * text.length * 2).px, (fontSizeInPx / 2).px)
+            var offset = paragraph.getOffsetForPosition(position)
+            assertThat(offset, equalTo(text.length))
 
             // negative
-            offset = Offset(-1 * fontSizeInPx, fontSizeInPx / 2)
-            position = paragraph.getPositionForOffset(offset)
-            assertThat(position, equalTo(0))
+            position = PxPosition((-1 * fontSizeInPx).px, (fontSizeInPx / 2).px)
+            offset = paragraph.getOffsetForPosition(position)
+            assertThat(offset, equalTo(0))
         }
     }
 
     @Test
-    fun getPositionForOffset_ltr_height_outOfBounds() {
+    fun getOffsetForPosition_ltr_height_outOfBounds() {
         withDensity(defaultDensity) {
             val text = "abc"
             val fontSize = 50.sp
@@ -337,14 +338,14 @@
             paragraph.layout(ParagraphConstraints(width = text.length * fontSizeInPx))
 
             // greater than height
-            var offset = Offset(fontSizeInPx / 2, fontSizeInPx * text.length * 2)
-            var position = paragraph.getPositionForOffset(offset)
-            assertThat(position, equalTo(0))
+            var position = PxPosition((fontSizeInPx / 2).px, (fontSizeInPx * text.length * 2).px)
+            var offset = paragraph.getOffsetForPosition(position)
+            assertThat(offset, equalTo(0))
 
             // negative
-            offset = Offset(fontSizeInPx / 2, -1 * fontSizeInPx)
-            position = paragraph.getPositionForOffset(offset)
-            assertThat(position, equalTo(0))
+            position = PxPosition((fontSizeInPx / 2).px, (-1 * fontSizeInPx).px)
+            offset = paragraph.getOffsetForPosition(position)
+            assertThat(offset, equalTo(0))
         }
     }
 
@@ -835,9 +836,9 @@
                 fontSize = fontSize
             )
             paragraph.layout(ParagraphConstraints(width = layoutWidth))
-            // The offset of the last character in display order.
-            val offset = Offset("a.".length * fontSizeInPx + 1, fontSizeInPx / 2)
-            val charIndex = paragraph.getPositionForOffset(offset = offset)
+            // 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))
         }
     }
@@ -856,9 +857,9 @@
                 fontSize = fontSize
             )
             paragraph.layout(ParagraphConstraints(width = layoutWidth))
-            // The offset of the first character in display order.
-            val offset = Offset(fontSizeInPx / 2 + 1, fontSizeInPx / 2)
-            val charIndex = paragraph.getPositionForOffset(offset = offset)
+            // 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))
         }
     }
@@ -877,9 +878,9 @@
             )
             paragraph.layout(ParagraphConstraints(width = layoutWidth))
             for (i in 0..text.length) {
-                // The offset of the i-th character in display order.
-                val offset = Offset(i * fontSizeInPx + 1, fontSizeInPx / 2)
-                val charIndex = paragraph.getPositionForOffset(offset = offset)
+                // 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))
             }
         }
@@ -899,9 +900,9 @@
             )
             paragraph.layout(ParagraphConstraints(width = layoutWidth))
             for (i in 0 until text.length) {
-                // The offset of the i-th character in display order.
-                val offset = Offset(i * fontSizeInPx + 1, fontSizeInPx / 2)
-                val charIndex = paragraph.getPositionForOffset(offset = offset)
+                // 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))
             }
         }
@@ -921,8 +922,8 @@
             )
             paragraph.layout(ParagraphConstraints(width = layoutWidth))
             // The first character in display order should be '.'
-            val offset = Offset(fontSizeInPx / 2 + 1, fontSizeInPx / 2)
-            val index = paragraph.getPositionForOffset(offset = offset)
+            val position = PxPosition((fontSizeInPx / 2 + 1).px, (fontSizeInPx / 2).px)
+            val index = paragraph.getOffsetForPosition(position)
             assertThat(index, equalTo(2))
         }
     }
@@ -1309,11 +1310,11 @@
             )
             paragraph.layout(ParagraphConstraints(width = Float.MAX_VALUE))
 
-            // This offset should point to the first character 'a' if indent is applied.
-            // Otherwise this offset will point to the second character 'b'.
-            val offset = Offset(indent + 1, fontSizeInPx / 2)
-            // The position corresponding to the offset should be the first char 'a'.
-            assertThat(paragraph.getPositionForOffset(offset), equalTo(0))
+            // 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))
         }
     }
 
@@ -1335,11 +1336,11 @@
             paragraph.layout(ParagraphConstraints(width = paragraphWidth))
 
             assertThat(paragraph.lineCount, equalTo(2))
-            // This offset should point to the first character of the first line if indent is
-            // applied. Otherwise this offset will point to the second character of the second line.
-            val offset = Offset(indent + 1, fontSizeInPx / 2)
-            // The position corresponding to the offset should be the first char 'a'.
-            assertThat(paragraph.getPositionForOffset(offset), equalTo(0))
+            // 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))
         }
     }
 
@@ -1363,12 +1364,12 @@
             )
             paragraph.layout(ParagraphConstraints(width = paragraphWidth))
 
-            // This offset should point to the first character of the second line if indent is
-            // applied. Otherwise this offset will point to the second character of the second line.
-            val offset = Offset(indent + 1, fontSizeInPx / 2 + fontSizeInPx)
-            // The position corresponding to the offset should be the 'd' in the second line.
+            // 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.getPositionForOffset(offset),
+                paragraph.getOffsetForPosition(position),
                 equalTo("abcd".length - 1)
             )
         }
@@ -1933,14 +1934,14 @@
     fun paint_throws_exception_if_layout_is_not_called() {
         val paragraph = simpleParagraph()
 
-        paragraph.paint(mock(), 0.0f, 0.0f)
+        paragraph.paint(mock())
     }
 
     @Test(expected = IllegalStateException::class)
-    fun getPositionForOffset_throws_exception_if_layout_is_not_called() {
+    fun getOffsetForPosition_throws_exception_if_layout_is_not_called() {
         val paragraph = simpleParagraph()
 
-        paragraph.getPositionForOffset(Offset(0.0f, 0.0f))
+        paragraph.getOffsetForPosition(PxPosition.Origin)
     }
 
     @Test(expected = AssertionError::class)
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 3e43115..41f1cf3 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
@@ -21,7 +21,9 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.ui.core.Constraints
 import androidx.ui.core.Density
+import androidx.ui.core.PxPosition
 import androidx.ui.core.ipx
+import androidx.ui.core.px
 import androidx.ui.core.sp
 import androidx.ui.core.withDensity
 import androidx.ui.engine.geometry.Offset
@@ -313,7 +315,7 @@
         )
         textPainter.layout(Constraints())
 
-        val selection = textPainter.getPositionForOffset(Offset(dx = 0f, dy = 0f))
+        val selection = textPainter.getOffsetForPosition(PxPosition.Origin)
 
         assertThat(selection).isEqualTo(0)
     }
@@ -342,8 +344,8 @@
             )
             textPainter.layout(Constraints())
 
-            val selection = textPainter.getPositionForOffset(
-                offset = Offset(dx = fontSize.toPx().value * characterIndex + 1f, dy = 0f)
+            val selection = textPainter.getOffsetForPosition(
+                position = PxPosition((fontSize.toPx().value * characterIndex + 1).px, 0.px)
             )
 
             assertThat(selection).isEqualTo(characterIndex)
@@ -489,8 +491,7 @@
                 start = 0,
                 end = text.length,
                 color = defaultSelectionColor,
-                canvas = actualCanvas,
-                offset = Offset.zero
+                canvas = actualCanvas
             )
 
             // Assert.
@@ -558,8 +559,7 @@
                 start = selectionStart,
                 end = selectionEnd,
                 color = defaultSelectionColor,
-                canvas = actualCanvas,
-                offset = Offset.zero
+                canvas = actualCanvas
             )
 
             // Assert
@@ -642,8 +642,7 @@
                 start = selectionLTRStart,
                 end = textLTR.length + selectionRTLEnd,
                 color = defaultSelectionColor,
-                canvas = actualCanvas,
-                offset = Offset.zero
+                canvas = actualCanvas
             )
 
             // Assert
@@ -711,8 +710,7 @@
                 start = selectionStart,
                 end = selectionEnd,
                 color = selectionColor,
-                canvas = actualCanvas,
-                offset = Offset.zero
+                canvas = actualCanvas
             )
 
             // Assert
diff --git a/ui/ui-text/src/androidTest/java/androidx/ui/text/TextTestExtensions.kt b/ui/ui-text/src/androidTest/java/androidx/ui/text/TextTestExtensions.kt
index 8d2746b..444b98d 100644
--- a/ui/ui-text/src/androidTest/java/androidx/ui/text/TextTestExtensions.kt
+++ b/ui/ui-text/src/androidTest/java/androidx/ui/text/TextTestExtensions.kt
@@ -30,7 +30,7 @@
         ceil(this.height).toInt(),
         Bitmap.Config.ARGB_8888
     )
-    this.paint(androidx.ui.painting.Canvas(Canvas(bitmap)), 0.0f, 0.0f)
+    this.paint(androidx.ui.painting.Canvas(Canvas(bitmap)))
     return bitmap
 }
 
diff --git a/ui/ui-text/src/androidTest/java/androidx/ui/text/platform/TextTestExtensions.kt b/ui/ui-text/src/androidTest/java/androidx/ui/text/platform/TextTestExtensions.kt
index 02aa82c..719f605 100644
--- a/ui/ui-text/src/androidTest/java/androidx/ui/text/platform/TextTestExtensions.kt
+++ b/ui/ui-text/src/androidTest/java/androidx/ui/text/platform/TextTestExtensions.kt
@@ -30,7 +30,7 @@
         ceil(this.height).toInt(),
         Bitmap.Config.ARGB_8888
     )
-    this.paint(androidx.ui.painting.Canvas(Canvas(bitmap)), 0.0f, 0.0f)
+    this.paint(androidx.ui.painting.Canvas(Canvas(bitmap)))
     return bitmap
 }
 
diff --git a/ui/ui-text/src/main/java/androidx/ui/input/KeyboardType.kt b/ui/ui-text/src/main/java/androidx/ui/input/KeyboardType.kt
index 1cd2750..ee7f088 100644
--- a/ui/ui-text/src/main/java/androidx/ui/input/KeyboardType.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/input/KeyboardType.kt
@@ -48,5 +48,15 @@
     /**
      * A keyboard type used to request an IME that is capable of inputting email addresses.
      */
-    Email
+    Email,
+
+    /**
+     * A keyboard type used to request an IME that is capable of inputting password
+     */
+    Password,
+
+    /**
+     * A keyboard type used to request an IME that is capable of inputting number password.
+     */
+    NumberPassword
 }
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/Paragraph.kt b/ui/ui-text/src/main/java/androidx/ui/text/Paragraph.kt
index 12814b0..a6a8156 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/Paragraph.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/Paragraph.kt
@@ -15,8 +15,9 @@
  */
 package androidx.ui.text
 
+import androidx.annotation.RestrictTo
 import androidx.ui.core.Density
-import androidx.ui.engine.geometry.Offset
+import androidx.ui.core.PxPosition
 import androidx.ui.engine.geometry.Rect
 import androidx.ui.painting.Canvas
 import androidx.ui.painting.Path
@@ -103,23 +104,51 @@
     /** Returns the right x Coordinate of the given line. */
     fun getLineRight(lineIndex: Int): Float
 
+    /**
+     * Returns the bottom y coordinate of the given line.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun getLineBottom(lineIndex: Int): Float
+
     /** Returns the height of the given line. */
     fun getLineHeight(lineIndex: Int): Float
 
     /** Returns the width of the given line. */
     fun getLineWidth(lineIndex: Int): Float
 
-    /** Returns the text position closest to the given offset. */
-    fun getPositionForOffset(offset: Offset): Int
+    /**
+     * 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.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun getLineForOffset(offset: Int): Int
 
     /**
-     * Returns the bounding box as Rect of the character for given offset. Rect includes the
-     * top, bottom, left and right of a character.
+     * Get the primary horizontal position for the specified text offset.
+     * @hide
      */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun getPrimaryHorizontal(offset: Int): Float
+
+    /** Returns the character offset closest to the given graphical position. */
+    fun getOffsetForPosition(position: PxPosition): Int
+
+    /**
+     * Returns the bounding box as Rect of the character for given character offset. Rect
+     * includes the top, bottom, left and right of a character.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
     fun getBoundingBox(offset: Int): Rect
 
     /**
-     * Returns the TextRange of the word at the given offset. Characters not
+     * 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
@@ -130,7 +159,7 @@
     /**
      * Paint the paragraph to canvas
      */
-    fun paint(canvas: Canvas, x: Float, y: Float)
+    fun paint(canvas: Canvas)
 }
 
 /*expect fun Paragraph(
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 e6e5b4d..7880c2d 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
@@ -22,6 +22,7 @@
 import androidx.ui.core.Constraints
 import androidx.ui.core.Density
 import androidx.ui.core.IntPxSize
+import androidx.ui.core.PxPosition
 import androidx.ui.core.Sp
 import androidx.ui.core.constrain
 import androidx.ui.core.px
@@ -383,7 +384,7 @@
     }
 
     /**
-     * Paints the text onto the given canvas at the given offset.
+     * Paints the text onto the given canvas.
      *
      * Valid only after [layout] has been called.
      *
@@ -395,7 +396,7 @@
      * To set the text style, specify a [TextStyle] when creating the [TextSpan] that you pass to
      * the [TextPainter] constructor or to the [text] property.
      */
-    fun paint(canvas: Canvas, offset: Offset) {
+    fun paint(canvas: Canvas) {
         assert(!needsLayout) {
             "TextPainter.paint called when text geometry was not yet calculated.\n" +
                     "Please call layout() before paint() to position the text before painting it."
@@ -415,7 +416,7 @@
         // layoutTextWithConstraints(constraints!!)
 
         if (hasVisualOverflow) {
-            val bounds = offset.and(size)
+            val bounds = Rect.fromLTWH(0f, 0f, size.width, size.height)
             if (overflowShader != null) {
                 // This layer limits what the shader below blends with to be just the text
                 // (as opposed to the text and its background).
@@ -425,14 +426,14 @@
             }
             canvas.clipRect(bounds)
         }
-        paragraph!!.paint(canvas, offset.dx, offset.dy)
+        paragraph!!.paint(canvas)
         if (hasVisualOverflow) {
             if (overflowShader != null) {
-                canvas.translate(offset.dx, offset.dy)
+                val bounds = Rect.fromLTWH(0f, 0f, size.width, size.height)
                 val paint = Paint()
                 paint.blendMode = BlendMode.multiply
                 paint.shader = overflowShader
-                canvas.drawRect(Offset.zero.and(size), paint)
+                canvas.drawRect(bounds, paint)
             }
             canvas.restore()
         }
@@ -443,23 +444,21 @@
      *
      * If the given range is empty, do nothing.
      *
-     * @param start inclusive start offset of the drawing range.
-     * @param end exclusive end offset of the drawing range.
+     * @param start inclusive start character offset of the drawing range.
+     * @param end exclusive end character offset of the drawing range.
      * @param color a color to be used for drawing background.
      * @param canvas the target canvas.
-     * @param offset the drawing offset.
      */
-    fun paintBackground(start: Int, end: Int, color: Color, canvas: Canvas, offset: Offset) {
+    fun paintBackground(start: Int, end: Int, color: Color, canvas: Canvas) {
         assert(!needsLayout)
         if (start == end) return
         val selectionPath = paragraph!!.getPathForRange(start, end)
-        selectionPath.shift(offset)
         // TODO(haoyuchang): check if move this paint to parameter is better
         canvas.drawPath(selectionPath, Paint().apply { this.color = color })
     }
 
     /**
-     * Draws the cursor at the given offset.
+     * Draws the cursor at the given character offset.
      *
      * TODO(nona): Make cursor customizable.
      *
@@ -472,15 +471,50 @@
         canvas.drawRect(cursorRect, Paint().apply { this.color = Color.Black })
     }
 
-    /** Returns the position within the text for the given pixel offset. */
-    fun getPositionForOffset(offset: Offset): Int {
+    /**
+     * Returns the bottom y coordinate of the given line.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun getLineBottom(lineIndex: Int): Float {
         assert(!needsLayout)
-        return paragraph!!.getPositionForOffset(offset)
+        return paragraph!!.getLineBottom(lineIndex)
     }
 
     /**
-     * Returns the bounding box as Rect of the character for given text position. Rect includes the
-     * top, bottom, left and right of a character.
+     * 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.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun getLineForOffset(offset: Int): Int {
+        assert(!needsLayout)
+        return paragraph!!.getLineForOffset(offset)
+    }
+
+    /**
+     * Get the primary horizontal position for the specified text offset.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun getPrimaryHorizontal(offset: Int): Float {
+        assert(!needsLayout)
+        return paragraph!!.getPrimaryHorizontal(offset)
+    }
+
+    /** Returns the character offset closest to the given graphical position. */
+    fun getOffsetForPosition(position: PxPosition): Int {
+        assert(!needsLayout)
+        return paragraph!!.getOffsetForPosition(position)
+    }
+
+    /**
+     * Returns the bounding box as Rect of the character for given character offset. Rect includes
+     * the top, bottom, left and right of a character.
      *
      * Valid only after [layout] has been called.
      *
@@ -493,15 +527,15 @@
     }
 
     /**
-     * Returns the text range of the word at the given 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 a text range that contains the given text position.
+     * Returns the text range 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 a text range that contains the given character offset.
      *
      * Word boundaries are defined more precisely in Unicode Standard Annex #29
      * <http://www.unicode.org/reports/tr29/#Word_Boundaries>.
      */
-    fun getWordBoundary(position: Int): TextRange {
+    fun getWordBoundary(offset: Int): TextRange {
         assert(!needsLayout)
-        return paragraph!!.getWordBoundary(position)
+        return paragraph!!.getWordBoundary(offset)
     }
-}
\ No newline at end of file
+}
diff --git a/ui/ui-text/src/main/java/androidx/ui/text/platform/AndroidParagraph.kt b/ui/ui-text/src/main/java/androidx/ui/text/platform/AndroidParagraph.kt
index c54d32b..054eb01 100644
--- a/ui/ui-text/src/main/java/androidx/ui/text/platform/AndroidParagraph.kt
+++ b/ui/ui-text/src/main/java/androidx/ui/text/platform/AndroidParagraph.kt
@@ -52,10 +52,10 @@
 import androidx.text.style.SkewXSpan
 import androidx.text.style.TypefaceSpan
 import androidx.ui.core.Density
+import androidx.ui.core.PxPosition
 import androidx.ui.text.TextRange
 import androidx.ui.core.px
 import androidx.ui.core.withDensity
-import androidx.ui.engine.geometry.Offset
 import androidx.ui.engine.geometry.Rect
 import androidx.ui.text.font.FontStyle
 import androidx.ui.text.font.FontSynthesis
@@ -203,14 +203,14 @@
         this.width = floorWidth
     }
 
-    override fun getPositionForOffset(offset: Offset): Int {
-        val line = ensureLayout.getLineForVertical(offset.dy.toInt())
-        return ensureLayout.getOffsetForHorizontal(line, offset.dx)
+    override fun getOffsetForPosition(position: PxPosition): Int {
+        val line = ensureLayout.getLineForVertical(position.y.value.toInt())
+        return ensureLayout.getOffsetForHorizontal(line, position.x.value)
     }
 
     /**
-     * Returns the bounding box as Rect of the character for given TextPosition. Rect includes the
-     * top, bottom, left and right of a character.
+     * Returns the bounding box as Rect of the character for given character offset. Rect includes
+     * the top, bottom, left and right of a character.
      */
     // TODO:(qqd) Implement RTL case.
     override fun getBoundingBox(offset: Int): Rect {
@@ -267,22 +267,27 @@
 
     override fun getLineRight(lineIndex: Int): Float = ensureLayout.getLineRight(lineIndex)
 
+    override fun getLineBottom(lineIndex: Int): Float = ensureLayout.getLineBottom(lineIndex)
+
     override fun getLineHeight(lineIndex: Int): Float = ensureLayout.getLineHeight(lineIndex)
 
     override fun getLineWidth(lineIndex: Int): Float = ensureLayout.getLineWidth(lineIndex)
 
+    override fun getLineForOffset(offset: Int): Int = ensureLayout.getLineForOffset(offset)
+
+    override fun getPrimaryHorizontal(offset: Int): Float =
+        ensureLayout.getPrimaryHorizontal(offset)
+
     /**
      * @return true if the given line is ellipsized, else false.
      */
     internal fun isEllipsisApplied(lineIndex: Int): Boolean =
         ensureLayout.isEllipsisApplied(lineIndex)
 
-    override fun paint(canvas: Canvas, x: Float, y: Float) {
+    override fun paint(canvas: Canvas) {
         val tmpLayout = layout ?: throw IllegalStateException("paint cannot be " +
                 "called before layout() is called")
-        canvas.translate(x, y)
         tmpLayout.paint(canvas.nativeCanvas)
-        canvas.translate(-x, -y)
     }
 
     private fun createTypeface(style: TextStyle): Typeface {
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 0738a48..6c07a79 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
@@ -18,7 +18,6 @@
 
 import androidx.ui.core.Constraints
 import androidx.ui.core.Density
-import androidx.ui.engine.geometry.Offset
 import androidx.ui.painting.Canvas
 import androidx.ui.text.font.Font
 import androidx.ui.text.style.TextAlign
@@ -223,6 +222,6 @@
         val textPainter = TextPainter(density = density, resourceLoader = resourceLoader)
         val canvas = mock<Canvas>()
 
-        textPainter.paint(canvas, Offset(0.0f, 0.0f))
+        textPainter.paint(canvas)
     }
 }
diff --git a/versionedparcelable/api/restricted_1.2.0-alpha01.ignore b/versionedparcelable/api/restricted_1.2.0-alpha01.ignore
index 4b76959..e7a06b2 100644
--- a/versionedparcelable/api/restricted_1.2.0-alpha01.ignore
+++ b/versionedparcelable/api/restricted_1.2.0-alpha01.ignore
@@ -1,6 +1,6 @@
 // Baseline format: 1.0
 ChangedType: androidx.versionedparcelable.VersionedParcel#mParcelizerCache:
-    Field androidx.versionedparcelable.VersionedParcel.mParcelizerCache has changed type from androidx.collection.ArrayMap<java.lang.String!,java.lang.Class!> to androidx.collection.ArrayMap<java.lang.String,java.lang.Class<?>>
+    Field androidx.versionedparcelable.VersionedParcel.mParcelizerCache has changed type from androidx.collection.ArrayMap<java.lang.String!,java.lang.Class!> to androidx.collection.ArrayMap<java.lang.String!,java.lang.Class<?>!>
 ChangedType: androidx.versionedparcelable.VersionedParcelize#factory():
     Method androidx.versionedparcelable.VersionedParcelize.factory has changed return type from Class to Class<?>
 
diff --git a/viewpager/build.gradle b/viewpager/build.gradle
index 2559de8..f1f7e65 100644
--- a/viewpager/build.gradle
+++ b/viewpager/build.gradle
@@ -17,7 +17,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    implementation("androidx.core:core:1.1.0-rc01")
+    implementation("androidx.core:core:1.1.0")
     api(project(":customview"))
 
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/viewpager2/api/1.0.0-beta03.txt b/viewpager2/api/1.0.0-beta03.txt
new file mode 100644
index 0000000..e1b6ab9
--- /dev/null
+++ b/viewpager2/api/1.0.0-beta03.txt
@@ -0,0 +1,96 @@
+// Signature format: 3.0
+package androidx.viewpager2.adapter {
+
+  public abstract class FragmentStateAdapter extends androidx.recyclerview.widget.RecyclerView.Adapter<androidx.viewpager2.adapter.FragmentViewHolder> implements androidx.viewpager2.adapter.StatefulAdapter {
+    ctor public FragmentStateAdapter(androidx.fragment.app.FragmentActivity);
+    ctor public FragmentStateAdapter(androidx.fragment.app.Fragment);
+    ctor public FragmentStateAdapter(androidx.fragment.app.FragmentManager, androidx.lifecycle.Lifecycle);
+    method public boolean containsItem(long);
+    method public abstract androidx.fragment.app.Fragment createFragment(int);
+    method public final void onBindViewHolder(androidx.viewpager2.adapter.FragmentViewHolder, int);
+    method public final androidx.viewpager2.adapter.FragmentViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public final boolean onFailedToRecycleView(androidx.viewpager2.adapter.FragmentViewHolder);
+    method public final void onViewAttachedToWindow(androidx.viewpager2.adapter.FragmentViewHolder);
+    method public final void onViewRecycled(androidx.viewpager2.adapter.FragmentViewHolder);
+    method public final void restoreState(android.os.Parcelable);
+    method public final android.os.Parcelable saveState();
+    method public final void setHasStableIds(boolean);
+  }
+
+  public final class FragmentViewHolder extends androidx.recyclerview.widget.RecyclerView.ViewHolder {
+  }
+
+  public interface StatefulAdapter {
+    method public void restoreState(android.os.Parcelable);
+    method public android.os.Parcelable saveState();
+  }
+
+}
+
+package androidx.viewpager2.widget {
+
+  public final class CompositePageTransformer implements androidx.viewpager2.widget.ViewPager2.PageTransformer {
+    ctor public CompositePageTransformer();
+    method public void addTransformer(androidx.viewpager2.widget.ViewPager2.PageTransformer);
+    method public void removeTransformer(androidx.viewpager2.widget.ViewPager2.PageTransformer);
+    method public void transformPage(android.view.View, float);
+  }
+
+  public final class MarginPageTransformer implements androidx.viewpager2.widget.ViewPager2.PageTransformer {
+    ctor public MarginPageTransformer(@Px int);
+    method public void transformPage(android.view.View, float);
+  }
+
+  public final class ViewPager2 extends android.view.ViewGroup {
+    ctor public ViewPager2(android.content.Context);
+    ctor public ViewPager2(android.content.Context, android.util.AttributeSet?);
+    ctor public ViewPager2(android.content.Context, android.util.AttributeSet?, int);
+    ctor @RequiresApi(21) public ViewPager2(android.content.Context, android.util.AttributeSet?, int, int);
+    method public void addItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration);
+    method public void addItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration, int);
+    method public boolean beginFakeDrag();
+    method public boolean endFakeDrag();
+    method public boolean fakeDragBy(@Px float);
+    method public androidx.recyclerview.widget.RecyclerView.Adapter? getAdapter();
+    method public int getCurrentItem();
+    method public androidx.recyclerview.widget.RecyclerView.ItemDecoration getItemDecorationAt(int);
+    method public int getItemDecorationCount();
+    method public int getOffscreenPageLimit();
+    method public int getOrientation();
+    method public int getScrollState();
+    method public void invalidateItemDecorations();
+    method public boolean isFakeDragging();
+    method public boolean isUserInputEnabled();
+    method public void registerOnPageChangeCallback(androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback);
+    method public void removeItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration);
+    method public void removeItemDecorationAt(int);
+    method public void requestTransform();
+    method public void setAdapter(androidx.recyclerview.widget.RecyclerView.Adapter?);
+    method public void setCurrentItem(int);
+    method public void setCurrentItem(int, boolean);
+    method public void setOffscreenPageLimit(int);
+    method public void setOrientation(int);
+    method public void setPageTransformer(androidx.viewpager2.widget.ViewPager2.PageTransformer?);
+    method public void setUserInputEnabled(boolean);
+    method public void unregisterOnPageChangeCallback(androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback);
+    field public static final int OFFSCREEN_PAGE_LIMIT_DEFAULT = -1; // 0xffffffff
+    field public static final int ORIENTATION_HORIZONTAL = 0; // 0x0
+    field public static final int ORIENTATION_VERTICAL = 1; // 0x1
+    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
+    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
+    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
+  }
+
+  public abstract static class ViewPager2.OnPageChangeCallback {
+    ctor public ViewPager2.OnPageChangeCallback();
+    method public void onPageScrollStateChanged(int);
+    method public void onPageScrolled(int, float, @Px int);
+    method public void onPageSelected(int);
+  }
+
+  public static interface ViewPager2.PageTransformer {
+    method public void transformPage(android.view.View, float);
+  }
+
+}
+
diff --git a/benchmark/api/res-1.0.0-alpha03.txt b/viewpager2/api/res-1.0.0-beta03.txt
similarity index 100%
copy from benchmark/api/res-1.0.0-alpha03.txt
copy to viewpager2/api/res-1.0.0-beta03.txt
diff --git a/viewpager2/api/restricted_1.0.0-beta03.txt b/viewpager2/api/restricted_1.0.0-beta03.txt
new file mode 100644
index 0000000..686a4b0
--- /dev/null
+++ b/viewpager2/api/restricted_1.0.0-beta03.txt
@@ -0,0 +1,105 @@
+// Signature format: 3.0
+package androidx.viewpager2.adapter {
+
+  public abstract class FragmentStateAdapter extends androidx.recyclerview.widget.RecyclerView.Adapter<androidx.viewpager2.adapter.FragmentViewHolder> implements androidx.viewpager2.adapter.StatefulAdapter {
+    ctor public FragmentStateAdapter(androidx.fragment.app.FragmentActivity);
+    ctor public FragmentStateAdapter(androidx.fragment.app.Fragment);
+    ctor public FragmentStateAdapter(androidx.fragment.app.FragmentManager, androidx.lifecycle.Lifecycle);
+    method public boolean containsItem(long);
+    method public abstract androidx.fragment.app.Fragment createFragment(int);
+    method public final void onBindViewHolder(androidx.viewpager2.adapter.FragmentViewHolder, int);
+    method public final androidx.viewpager2.adapter.FragmentViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+    method public final boolean onFailedToRecycleView(androidx.viewpager2.adapter.FragmentViewHolder);
+    method public final void onViewAttachedToWindow(androidx.viewpager2.adapter.FragmentViewHolder);
+    method public final void onViewRecycled(androidx.viewpager2.adapter.FragmentViewHolder);
+    method public final void restoreState(android.os.Parcelable);
+    method public final android.os.Parcelable saveState();
+    method public final void setHasStableIds(boolean);
+  }
+
+  public final class FragmentViewHolder extends androidx.recyclerview.widget.RecyclerView.ViewHolder {
+  }
+
+  public interface StatefulAdapter {
+    method public void restoreState(android.os.Parcelable);
+    method public android.os.Parcelable saveState();
+  }
+
+}
+
+package androidx.viewpager2.widget {
+
+  public final class CompositePageTransformer implements androidx.viewpager2.widget.ViewPager2.PageTransformer {
+    ctor public CompositePageTransformer();
+    method public void addTransformer(androidx.viewpager2.widget.ViewPager2.PageTransformer);
+    method public void removeTransformer(androidx.viewpager2.widget.ViewPager2.PageTransformer);
+    method public void transformPage(android.view.View, float);
+  }
+
+  public final class MarginPageTransformer implements androidx.viewpager2.widget.ViewPager2.PageTransformer {
+    ctor public MarginPageTransformer(@Px int);
+    method public void transformPage(android.view.View, float);
+  }
+
+  public final class ViewPager2 extends android.view.ViewGroup {
+    ctor public ViewPager2(android.content.Context);
+    ctor public ViewPager2(android.content.Context, android.util.AttributeSet?);
+    ctor public ViewPager2(android.content.Context, android.util.AttributeSet?, int);
+    ctor @RequiresApi(21) public ViewPager2(android.content.Context, android.util.AttributeSet?, int, int);
+    method public void addItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration);
+    method public void addItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration, int);
+    method public boolean beginFakeDrag();
+    method public boolean endFakeDrag();
+    method public boolean fakeDragBy(@Px float);
+    method public androidx.recyclerview.widget.RecyclerView.Adapter? getAdapter();
+    method public int getCurrentItem();
+    method public androidx.recyclerview.widget.RecyclerView.ItemDecoration getItemDecorationAt(int);
+    method public int getItemDecorationCount();
+    method @androidx.viewpager2.widget.ViewPager2.OffscreenPageLimit public int getOffscreenPageLimit();
+    method @androidx.viewpager2.widget.ViewPager2.Orientation public int getOrientation();
+    method @androidx.viewpager2.widget.ViewPager2.ScrollState public int getScrollState();
+    method public void invalidateItemDecorations();
+    method public boolean isFakeDragging();
+    method public boolean isUserInputEnabled();
+    method public void registerOnPageChangeCallback(androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback);
+    method public void removeItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration);
+    method public void removeItemDecorationAt(int);
+    method public void requestTransform();
+    method public void setAdapter(androidx.recyclerview.widget.RecyclerView.Adapter?);
+    method public void setCurrentItem(int);
+    method public void setCurrentItem(int, boolean);
+    method public void setOffscreenPageLimit(@androidx.viewpager2.widget.ViewPager2.OffscreenPageLimit int);
+    method public void setOrientation(@androidx.viewpager2.widget.ViewPager2.Orientation int);
+    method public void setPageTransformer(androidx.viewpager2.widget.ViewPager2.PageTransformer?);
+    method public void setUserInputEnabled(boolean);
+    method public void unregisterOnPageChangeCallback(androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback);
+    field public static final int OFFSCREEN_PAGE_LIMIT_DEFAULT = -1; // 0xffffffff
+    field public static final int ORIENTATION_HORIZONTAL = 0; // 0x0
+    field public static final int ORIENTATION_VERTICAL = 1; // 0x1
+    field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
+    field public static final int SCROLL_STATE_IDLE = 0; // 0x0
+    field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
+  }
+
+  @IntDef({androidx.viewpager2.widget.ViewPager2.OFFSCREEN_PAGE_LIMIT_DEFAULT}) @IntRange(from=1) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewPager2.OffscreenPageLimit {
+  }
+
+  public abstract static class ViewPager2.OnPageChangeCallback {
+    ctor public ViewPager2.OnPageChangeCallback();
+    method public void onPageScrollStateChanged(@androidx.viewpager2.widget.ViewPager2.ScrollState int);
+    method public void onPageScrolled(int, float, @Px int);
+    method public void onPageSelected(int);
+  }
+
+  @IntDef({androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL, androidx.viewpager2.widget.ViewPager2.ORIENTATION_VERTICAL}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewPager2.Orientation {
+  }
+
+  public static interface ViewPager2.PageTransformer {
+    method public void transformPage(android.view.View, float);
+  }
+
+  @IntDef({androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_IDLE, androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_DRAGGING, androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_SETTLING}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewPager2.ScrollState {
+  }
+
+}
+
diff --git a/viewpager2/build.gradle b/viewpager2/build.gradle
index 9de6aa7..29ea26d 100644
--- a/viewpager2/build.gradle
+++ b/viewpager2/build.gradle
@@ -27,7 +27,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    implementation("androidx.core:core:1.1.0-rc01")
+    implementation("androidx.core:core:1.1.0")
     api("androidx.fragment:fragment:1.1.0-rc01")
     api("androidx.recyclerview:recyclerview:1.1.0-beta01")
     implementation("androidx.collection:collection:1.1.0")
diff --git a/viewpager2/integration-tests/testapp/build.gradle b/viewpager2/integration-tests/testapp/build.gradle
index 0452879..00fbbe0 100644
--- a/viewpager2/integration-tests/testapp/build.gradle
+++ b/viewpager2/integration-tests/testapp/build.gradle
@@ -16,7 +16,6 @@
 
 import static androidx.build.dependencies.DependenciesKt.ESPRESSO_CORE
 import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
-import static androidx.build.dependencies.DependenciesKt.MATERIAL
 import static androidx.build.dependencies.DependenciesKt.ANDROIDX_TEST_EXT_JUNIT
 import static androidx.build.dependencies.DependenciesKt.ANDROIDX_TEST_RULES
 
@@ -37,10 +36,10 @@
 dependencies {
     api(KOTLIN_STDLIB)
     implementation(project(":viewpager2"))
-    implementation(project(":fragment:fragment-ktx"))
-    implementation(MATERIAL)
-    implementation(project(":coordinatorlayout"))
-    implementation(project(":cardview"))
+    implementation("androidx.activity:activity-ktx:1.0.0-rc01")
+    implementation("com.google.android.material:material:1.1.0-alpha08") {
+        exclude group: 'androidx.viewpager2', module: 'viewpager2'
+    }
 
     androidTestImplementation(ANDROIDX_TEST_RULES)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/CardViewTabLayoutActivity.kt b/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/CardViewTabLayoutActivity.kt
index f04eaba..b969f25 100644
--- a/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/CardViewTabLayoutActivity.kt
+++ b/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/CardViewTabLayoutActivity.kt
@@ -19,6 +19,7 @@
 import android.os.Bundle
 import androidx.viewpager2.integration.testapp.cards.Card
 import com.google.android.material.tabs.TabLayout
+import com.google.android.material.tabs.TabLayoutMediator
 
 class CardViewTabLayoutActivity : CardViewActivity() {
 
diff --git a/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/TabLayoutMediator.java b/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/TabLayoutMediator.java
deleted file mode 100644
index 4a63e42..0000000
--- a/viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/TabLayoutMediator.java
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.viewpager2.integration.testapp;
-
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_DRAGGING;
-import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_IDLE;
-import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_SETTLING;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.viewpager2.widget.ViewPager2;
-
-import com.google.android.material.tabs.TabLayout;
-
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Method;
-
-/**
- * A mediator to link a TabLayout with a ViewPager2. The mediator will synchronize the ViewPager2's
- * position with the selected tab when a tab is selected, and the TabLayout's scroll position when
- * the user drags the ViewPager2.
- *
- * Establish the link by creating an instance of this class, make sure the ViewPager2 has an adapter
- * and then call {@link #attach()} on it. When creating an instance of this class, you must supply
- * an implementation of {@link OnConfigureTabCallback} in which you set the text of the tab, and/or
- * perform any styling of the tabs that you require.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public final class TabLayoutMediator {
-    private final @NonNull TabLayout mTabLayout;
-    private final @NonNull ViewPager2 mViewPager;
-    private final boolean mAutoRefresh;
-    private final OnConfigureTabCallback mOnConfigureTabCallback;
-    private RecyclerView.Adapter mAdapter;
-    private boolean mAttached;
-
-    private TabLayoutOnPageChangeCallback mOnPageChangeCallback;
-    private TabLayout.OnTabSelectedListener mOnTabSelectedListener;
-    private RecyclerView.AdapterDataObserver mPagerAdapterObserver;
-
-    /**
-     * A callback interface that must be implemented to set the text and styling of newly created
-     * tabs.
-     */
-    public interface OnConfigureTabCallback {
-        /**
-         * Called to configure the tab for the page at the specified position. Typically calls
-         * {@link TabLayout.Tab#setText(CharSequence)}, but any form of styling can be applied.
-         *
-         * @param tab The Tab which should be configured to represent the title of the item at the
-         *        given position in the data set.
-         * @param position The position of the item within the adapter's data set.
-         */
-        void onConfigureTab(@NonNull TabLayout.Tab tab, int position);
-    }
-
-    /**
-     * Creates a TabLayoutMediator to synchronize a TabLayout and a ViewPager2 together. It will
-     * update the tabs automatically when the data set of the view pager's adapter changes. The link
-     * will be established after {@link #attach()} is called.
-     *
-     * @param tabLayout The tab bar to link
-     * @param viewPager The view pager to link
-     */
-    public TabLayoutMediator(@NonNull TabLayout tabLayout, @NonNull ViewPager2 viewPager,
-            @NonNull OnConfigureTabCallback onConfigureTabCallback) {
-        this(tabLayout, viewPager, true, onConfigureTabCallback);
-    }
-
-    /**
-     * Creates a TabLayoutMediator to synchronize a TabLayout and a ViewPager2 together. If {@code
-     * autoRefresh} is true, it will update the tabs automatically when the data set of the view
-     * pager's adapter changes. The link will be established after {@link #attach()} is called.
-     *
-     * @param tabLayout The tab bar to link
-     * @param viewPager The view pager to link
-     * @param autoRefresh If {@code true}, will recreate all tabs when the data set of the view
-     *                   pager's adapter changes.
-     */
-    public TabLayoutMediator(@NonNull TabLayout tabLayout, @NonNull ViewPager2 viewPager,
-            boolean autoRefresh, @NonNull OnConfigureTabCallback onConfigureTabCallback) {
-        mTabLayout = tabLayout;
-        mViewPager = viewPager;
-        mAutoRefresh = autoRefresh;
-        mOnConfigureTabCallback = onConfigureTabCallback;
-    }
-
-    /**
-     * Link the TabLayout and the ViewPager2 together.
-     * @throws IllegalStateException If the mediator is already attached, or the ViewPager2 has no
-     *         adapter.
-     */
-    public void attach() {
-        if (mAttached) {
-            throw new IllegalStateException("TabLayoutMediator is already attached");
-        }
-        mAdapter = mViewPager.getAdapter();
-        if (mAdapter == null) {
-            throw new IllegalStateException("TabLayoutMediator attached before ViewPager2 has an "
-                    + "adapter");
-        }
-        mAttached = true;
-
-        // Add our custom OnPageChangeCallback to the ViewPager
-        mOnPageChangeCallback = new TabLayoutOnPageChangeCallback(mTabLayout);
-        mViewPager.registerOnPageChangeCallback(mOnPageChangeCallback);
-
-        // Now we'll add a tab selected listener to set ViewPager's current item
-        mOnTabSelectedListener = new ViewPagerOnTabSelectedListener(mViewPager);
-        mTabLayout.addOnTabSelectedListener(mOnTabSelectedListener);
-
-        // Now we'll populate ourselves from the pager adapter, adding an observer if
-        // autoRefresh is enabled
-        if (mAutoRefresh) {
-            // Register our observer on the new adapter
-            mPagerAdapterObserver = new PagerAdapterObserver();
-            mAdapter.registerAdapterDataObserver(mPagerAdapterObserver);
-        }
-
-        populateTabsFromPagerAdapter();
-
-        // Now update the scroll position to match the ViewPager's current item
-        mTabLayout.setScrollPosition(mViewPager.getCurrentItem(), 0f, true);
-    }
-
-    /**
-     * Unlink the TabLayout and the ViewPager
-     */
-    public void detach() {
-        mAdapter.unregisterAdapterDataObserver(mPagerAdapterObserver);
-        mTabLayout.removeOnTabSelectedListener(mOnTabSelectedListener);
-        mViewPager.unregisterOnPageChangeCallback(mOnPageChangeCallback);
-        mPagerAdapterObserver = null;
-        mOnTabSelectedListener = null;
-        mOnPageChangeCallback = null;
-        mAttached = false;
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    void populateTabsFromPagerAdapter() {
-        mTabLayout.removeAllTabs();
-
-        if (mAdapter != null) {
-            int adapterCount = mAdapter.getItemCount();
-            for (int i = 0; i < adapterCount; i++) {
-                TabLayout.Tab tab = mTabLayout.newTab();
-                mOnConfigureTabCallback.onConfigureTab(tab, i);
-                mTabLayout.addTab(tab, false);
-            }
-
-            // Make sure we reflect the currently set ViewPager item
-            if (adapterCount > 0) {
-                int currItem = mViewPager.getCurrentItem();
-                if (currItem != mTabLayout.getSelectedTabPosition()) {
-                    mTabLayout.getTabAt(currItem).select();
-                }
-            }
-        }
-    }
-
-    /**
-     * A {@link ViewPager2.OnPageChangeCallback} class which contains the necessary calls back to
-     * the provided {@link TabLayout} so that the tab position is kept in sync.
-     *
-     * <p>This class stores the provided TabLayout weakly, meaning that you can use {@link
-     * ViewPager2#registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback)} without removing
-     * the callback and not cause a leak.
-     */
-    private static class TabLayoutOnPageChangeCallback extends ViewPager2.OnPageChangeCallback {
-        private final WeakReference<TabLayout> mTabLayoutRef;
-        private int mPreviousScrollState;
-        private int mScrollState;
-
-        TabLayoutOnPageChangeCallback(TabLayout tabLayout) {
-            mTabLayoutRef = new WeakReference<>(tabLayout);
-            reset();
-        }
-
-        @Override
-        public void onPageScrollStateChanged(final int state) {
-            mPreviousScrollState = mScrollState;
-            mScrollState = state;
-        }
-
-        @Override
-        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
-            TabLayout tabLayout = mTabLayoutRef.get();
-            if (tabLayout != null) {
-                // Only update the text selection if we're not settling, or we are settling after
-                // being dragged
-                boolean updateText = mScrollState != SCROLL_STATE_SETTLING
-                        || mPreviousScrollState == SCROLL_STATE_DRAGGING;
-                // Update the indicator if we're not settling after being idle. This is caused
-                // from a setCurrentItem() call and will be handled by an animation from
-                // onPageSelected() instead.
-                boolean updateIndicator = !(mScrollState == SCROLL_STATE_SETTLING
-                        && mPreviousScrollState == SCROLL_STATE_IDLE);
-                setScrollPosition(tabLayout, position, positionOffset, updateText, updateIndicator);
-            }
-        }
-
-        @Override
-        public void onPageSelected(final int position) {
-            TabLayout tabLayout = mTabLayoutRef.get();
-            if (tabLayout != null
-                    && tabLayout.getSelectedTabPosition() != position
-                    && position < tabLayout.getTabCount()) {
-                // Select the tab, only updating the indicator if we're not being dragged/settled
-                // (since onPageScrolled will handle that).
-                boolean updateIndicator = mScrollState == SCROLL_STATE_IDLE
-                        || (mScrollState == SCROLL_STATE_SETTLING
-                        && mPreviousScrollState == SCROLL_STATE_IDLE);
-                selectTab(tabLayout, tabLayout.getTabAt(position), updateIndicator);
-            }
-        }
-
-        void reset() {
-            mPreviousScrollState = mScrollState = SCROLL_STATE_IDLE;
-        }
-    }
-
-    // region Reflective calls
-
-    // Temporarily call methods TabLayout.setScrollPosition(int, float, boolean, boolean) and
-    // TabLayout.selectTab(TabLayout.Tab, boolean) through reflection, until they have been made
-    // public in the Material Design Components library.
-
-    private static Method sSetScrollPosition;
-    private static Method sSelectTab;
-    private static final String SET_SCROLL_POSITION_NAME = "TabLayout.setScrollPosition(int, float,"
-            + " boolean, boolean)";
-    private static final String SELECT_TAB_NAME = "TabLayout.selectTab(TabLayout.Tab, boolean)";
-
-    static {
-        try {
-            sSetScrollPosition = TabLayout.class.getDeclaredMethod("setScrollPosition", int.class,
-                    float.class, boolean.class, boolean.class);
-            sSetScrollPosition.setAccessible(true);
-
-            sSelectTab = TabLayout.class.getDeclaredMethod("selectTab", TabLayout.Tab.class,
-                    boolean.class);
-            sSelectTab.setAccessible(true);
-        } catch (NoSuchMethodException e) {
-            throw new IllegalStateException("Can't reflect into method TabLayout"
-                    + ".setScrollPosition(int, float, boolean, boolean)");
-        }
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    static void setScrollPosition(TabLayout tabLayout, int position, float positionOffset,
-            boolean updateSelectedText, boolean updateIndicatorPosition) {
-        try {
-            if (sSetScrollPosition != null) {
-                sSetScrollPosition.invoke(tabLayout, position, positionOffset, updateSelectedText,
-                        updateIndicatorPosition);
-            } else {
-                throwMethodNotFound(SET_SCROLL_POSITION_NAME);
-            }
-        } catch (Exception e) {
-            throwInvokeFailed(SET_SCROLL_POSITION_NAME);
-        }
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    static void selectTab(TabLayout tabLayout, TabLayout.Tab tab, boolean updateIndicator) {
-        try {
-            if (sSelectTab != null) {
-                sSelectTab.invoke(tabLayout, tab, updateIndicator);
-            } else {
-                throwMethodNotFound(SELECT_TAB_NAME);
-            }
-        } catch (Exception e) {
-            throwInvokeFailed(SELECT_TAB_NAME);
-        }
-    }
-
-    private static void throwMethodNotFound(String method) {
-        throw new IllegalStateException("Method " + method + " not found");
-    }
-
-    private static void throwInvokeFailed(String method) {
-        throw new IllegalStateException("Couldn't invoke method " + method);
-    }
-
-    // endregion
-
-    /**
-     * A {@link TabLayout.OnTabSelectedListener} class which contains the necessary calls back to
-     * the provided {@link ViewPager2} so that the tab position is kept in sync.
-     */
-    private static class ViewPagerOnTabSelectedListener implements TabLayout.OnTabSelectedListener {
-        private final ViewPager2 mViewPager;
-
-        ViewPagerOnTabSelectedListener(ViewPager2 viewPager) {
-            this.mViewPager = viewPager;
-        }
-
-        @Override
-        public void onTabSelected(TabLayout.Tab tab) {
-            mViewPager.setCurrentItem(tab.getPosition(), true);
-        }
-
-        @Override
-        public void onTabUnselected(TabLayout.Tab tab) {
-            // No-op
-        }
-
-        @Override
-        public void onTabReselected(TabLayout.Tab tab) {
-            // No-op
-        }
-    }
-
-    private class PagerAdapterObserver extends RecyclerView.AdapterDataObserver {
-        PagerAdapterObserver() {}
-
-        @Override
-        public void onChanged() {
-            populateTabsFromPagerAdapter();
-        }
-
-        @Override
-        public void onItemRangeChanged(int positionStart, int itemCount) {
-            populateTabsFromPagerAdapter();
-        }
-
-        @Override
-        public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
-            populateTabsFromPagerAdapter();
-        }
-
-        @Override
-        public void onItemRangeInserted(int positionStart, int itemCount) {
-            populateTabsFromPagerAdapter();
-        }
-
-        @Override
-        public void onItemRangeRemoved(int positionStart, int itemCount) {
-            populateTabsFromPagerAdapter();
-        }
-
-        @Override
-        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
-            populateTabsFromPagerAdapter();
-        }
-    }
-}
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/SparseAdapter.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/SparseAdapter.kt
new file mode 100644
index 0000000..1b770d3
--- /dev/null
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/SparseAdapter.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.viewpager2.test.ui
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewpager2.test.R
+
+class SparseAdapter(private val size: Int) : RecyclerView.Adapter<SparseAdapter.TextViewHolder>() {
+    override fun getItemCount(): Int = size
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextViewHolder {
+        val inflater = LayoutInflater.from(parent.context)
+        val view = inflater.inflate(R.layout.item_test_layout, parent, false)
+        return TextViewHolder(view as TextView)
+    }
+
+    override fun onBindViewHolder(holder: TextViewHolder, position: Int) {
+        holder.textView.text = "$position"
+    }
+
+    class TextViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView)
+}
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/TouchConsumingTextView.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/TouchConsumingTextView.kt
index 0061566..3372525a 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/TouchConsumingTextView.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/test/ui/TouchConsumingTextView.kt
@@ -21,11 +21,11 @@
 import android.view.MotionEvent
 import androidx.appcompat.widget.AppCompatTextView
 
-class TouchConsumingTextView(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) :
+class TouchConsumingTextView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
     AppCompatTextView(context, attrs, defStyleAttr) {
 
-    constructor(context: Context?) : this(context, null, 0)
-    constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)
+    constructor(context: Context) : this(context, null, 0)
+    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
 
     var consumeTouches = false
 
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
index 7d6e3a3..874da2c 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/FakeDragTest.kt
@@ -191,7 +191,7 @@
             val tracker = PositionTracker().also { test.viewPager.registerOnPageChangeCallback(it) }
             val targetPage = test.viewPager.currentItem + 1
             startFakeDragWhileSettling(targetPage,
-                { targetPage - tracker.lastPosition }, targetPage, true)
+                { (targetPage - tracker.lastPosition).toFloat() }, targetPage, true)
             test.viewPager.unregisterOnPageChangeCallback(tracker)
         }
     }
@@ -213,7 +213,7 @@
             val targetPage = test.viewPager.currentItem + 1
             val nextPage = targetPage + 1
             startFakeDragWhileSettling(targetPage,
-                { nextPage - tracker.lastPosition }, nextPage, true)
+                { (nextPage - tracker.lastPosition).toFloat() }, nextPage, true)
             test.viewPager.unregisterOnPageChangeCallback(tracker)
         }
     }
@@ -425,11 +425,11 @@
                             "state already left SETTLING")
                 }
                 // Check 2: setCurrentItem should not have finished
-                if (settleTarget - currPosition <= 0f) {
+                if (settleTarget - currPosition <= 0) {
                     throw RetryException("Interruption of SETTLING too late: already at target")
                 }
                 // Check 3: fake drag should not overshoot its target
-                if (expectedFinalPage - currPosition < dragDistance) {
+                if ((expectedFinalPage - currPosition).toFloat() < dragDistance) {
                     throw RetryException("Interruption of SETTLING too late: already closer than " +
                             "$dragDistance from target")
                 }
@@ -556,10 +556,10 @@
         }
     }
 
-    private val ViewPager2.relativeScrollPosition: Float
+    private val ViewPager2.relativeScrollPosition: Double
         get() {
             val scrollEventAdapter = mScrollEventAdapterField.get(this)
-            return getRelativeScrollPositionMethod.invoke(scrollEventAdapter) as Float
+            return getRelativeScrollPositionMethod.invoke(scrollEventAdapter) as Double
         }
 
     private fun ViewPager2.addNewRecordingCallback(): RecordingCallback {
@@ -687,16 +687,16 @@
         otherPage: Int,
         pageSize: Int
     ) = forEach {
-        assertThat(it.position + it.positionOffset,
-            isBetweenInInMinMax(initialPage.toFloat(), otherPage.toFloat()))
+        assertThat(it.position + it.positionOffset.toDouble(),
+            isBetweenInInMinMax(initialPage.toDouble(), otherPage.toDouble()))
         assertThat(it.positionOffset, isBetweenInEx(0f, 1f))
         assertThat((it.positionOffset * pageSize).roundToInt(), equalTo(it.positionOffsetPixels))
     }
 
     private class PositionTracker : ViewPager2.OnPageChangeCallback() {
-        var lastPosition = 0f
+        var lastPosition = 0.0
         override fun onPageScrolled(position: Int, offset: Float, offsetPx: Int) {
-            lastPosition = position + offset
+            lastPosition = position + offset.toDouble()
         }
     }
 }
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt
index 8fae152..18a3c27 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/OffscreenPageLimitTest.kt
@@ -117,12 +117,12 @@
                 is OnChildViewRemoved -> assertThat(onscreen.remove(event.position), equalTo(true))
                 // When VP2 scrolls, check if the set of onscreen pages is the expected value
                 is OnPageScrolledEvent -> {
-                    val position = event.position + event.positionOffset
+                    val position = event.position + event.positionOffset.toDouble()
                     val lower = max(0, floor(position - limit).roundToInt())
                     val upper = min(pageCount - 1, ceil(position + limit).roundToInt())
                     // First verify this calculation:
-                    assertThat(lower.toFloat(), lessThanOrEqualTo(position))
-                    assertThat(upper.toFloat(), greaterThanOrEqualTo(position))
+                    assertThat(lower.toDouble(), lessThanOrEqualTo(position))
+                    assertThat(upper.toDouble(), greaterThanOrEqualTo(position))
                     // Then verify the onscreen pages:
                     assertThat("There should be ${upper - lower + 1} pages laid out at event $i. " +
                             "Events: ${recorder.dumpEvents()}",
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
index f257a8e2..81b6edd 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageChangeCallbackTest.kt
@@ -28,6 +28,7 @@
 import androidx.testutils.PollingCheck
 import androidx.testutils.waitForExecution
 import androidx.viewpager.widget.ViewPager
+import androidx.viewpager2.test.ui.SparseAdapter
 import androidx.viewpager2.widget.BaseTest.Context.SwipeMethod
 import androidx.viewpager2.widget.PageChangeCallbackTest.Event.MarkerEvent
 import androidx.viewpager2.widget.PageChangeCallbackTest.Event.OnPageScrollStateChangedEvent
@@ -989,6 +990,48 @@
         // no crash
     }
 
+    @Test
+    fun test_setCurrentItem_maxIntItems() {
+        val test = setUpTest(config.orientation)
+        test.setAdapterSync { SparseAdapter(Int.MAX_VALUE) }
+        test.assertBasicState(0)
+
+        val testPages = listOf(1073742144, (1 shl 25) + 2, Int.MAX_VALUE - 2)
+
+        val recorder = test.viewPager.addNewRecordingCallback()
+        testPages.forEach { targetPage ->
+            test.viewPager.setCurrentItemSync(targetPage, false, 2, SECONDS)
+            test.assertBasicState(targetPage)
+            test.viewPager.setCurrentItemSync(targetPage + 1, true, 2, SECONDS)
+            test.assertBasicState(targetPage + 1)
+        }
+
+        recorder.assertScrollsAreBetweenSelectedPages()
+        recorder.assertAllPagesSelected(testPages.flatMap { listOf(it, it + 1) })
+    }
+
+    @Test
+    fun test_setCurrentItemWhileScrolling_maxIntItems() {
+        val test = setUpTest(config.orientation)
+        test.setAdapterSync { SparseAdapter(Int.MAX_VALUE) }
+        test.assertBasicState(0)
+
+        val targetPage = 1073742144
+
+        val recorder = test.viewPager.addNewRecordingCallback()
+        val distanceLatch = test.viewPager.addWaitForDistanceToTarget(targetPage, 1.5f)
+        test.runOnUiThread {
+            test.viewPager.setCurrentItem(targetPage, true)
+        }
+
+        distanceLatch.await(2, SECONDS)
+        test.viewPager.setCurrentItemSync(targetPage + 1, true, 2, SECONDS)
+        test.assertBasicState(targetPage + 1)
+
+        recorder.assertScrollsAreBetweenSelectedPages()
+        recorder.assertAllPagesSelected(listOf(targetPage, targetPage + 1))
+    }
+
     private fun ViewPager2.addNewRecordingCallback(): RecordingCallback {
         return RecordingCallback().also { registerOnPageChangeCallback(it) }
     }
@@ -1084,15 +1127,15 @@
 
     private fun RecordingCallback.assertScrollsAreBetweenSelectedPages() {
         var selectedPage = -1
-        var prevScrollPosition = 0f
+        var prevScrollPosition = 0.0
         scrollAndSelectEvents.forEach { event ->
             when (event) {
                 is OnPageSelectedEvent -> selectedPage = event.position
                 is OnPageScrolledEvent -> {
                     assertThat(selectedPage, not(equalTo(-1)))
-                    val currScrollPosition = event.position + event.positionOffset
+                    val currScrollPosition = event.position + event.positionOffset.toDouble()
                     assertThat(currScrollPosition,
-                        isBetweenInIn(prevScrollPosition, selectedPage.toFloat()))
+                        isBetweenInInMinMax(prevScrollPosition, selectedPage.toDouble()))
                     prevScrollPosition = currScrollPosition
                 }
             }
@@ -1141,7 +1184,7 @@
     }
 
     private fun List<OnPageScrolledEvent>.assertOffsetSorted(sortOrder: SortOrder) {
-        map { it.position + it.positionOffset }.assertSorted { it * sortOrder.sign }
+        map { it.position + it.positionOffset.toDouble() }.assertSorted { it * sortOrder.sign }
     }
 
     private fun List<OnPageScrolledEvent>.assertMaxShownPages() {
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt
index 18837c4..5981d6b 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/PageTransformerTest.kt
@@ -17,7 +17,6 @@
 package androidx.viewpager2.widget
 
 import android.view.View
-import androidx.annotation.FloatRange
 import androidx.annotation.NonNull
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.test.filters.LargeTest
@@ -234,10 +233,7 @@
 
         /* interface implementations */
 
-        override fun transformPage(
-            @NonNull page: View,
-            @FloatRange(from = -1.0, to = 1.0) position: Float
-        ) {
+        override fun transformPage(@NonNull page: View, position: Float) {
             events.add(TransformPageEvent(layoutManager.getPosition(page), position))
         }
 
@@ -270,7 +266,7 @@
         map { it as TransformPageEvent }.forEach {
             assertThat("transformPage() call must be snapped at page $snappedPage",
                 // event.page - event.offset resolves to the currently visible page index
-                it.page - it.offset, equalTo(snappedPage.toFloat())
+                it.page - it.offset.toDouble(), equalTo(snappedPage.toDouble())
             )
         }
     }
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt
index 5aa3356..3f4e84f 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/SetItemWhileScrollInProgressTest.kt
@@ -247,19 +247,19 @@
 
     private fun RecordingCallback.assertScrollTowardsSelectedPage() {
         var target = 0
-        var prevPosition = 0f
+        var prevPosition = 0.0
         events.forEach {
             when (it) {
                 is OnPageSelectedEvent -> target = it.position
-                is Event.OnPageScrolledEvent -> {
-                    val currentPosition = it.position + it.positionOffset
+                is OnPageScrolledEvent -> {
+                    val currentPosition = it.position + it.positionOffset.toDouble()
                     assertThat(
                         "Scroll event fired before page selected event",
                         target, not(equalTo(-1))
                     )
                     assertThat(
                         "Scroll event not between start and destination",
-                        currentPosition, isBetweenInInMinMax(prevPosition, target.toFloat())
+                        currentPosition, isBetweenInInMinMax(prevPosition, target.toDouble())
                     )
                     prevPosition = currentPosition
                 }
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.kt
new file mode 100644
index 0000000..4a3337c
--- /dev/null
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/TransientStateFragmentTest.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.viewpager2.widget
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit.MILLISECONDS
+
+/**
+ * Verifies that [androidx.viewpager2.adapter.FragmentStateAdapter] can handle [Fragment]s
+ * having transient state.
+ */
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+class TransientStateFragmentTest : BaseTest() {
+    private val orientation = ORIENTATION_HORIZONTAL
+    private val totalPages = 10
+    private val adapterProvider = fragmentAdapterProviderValueId
+    private val timeoutMs = 3000L
+
+    @Test
+    fun test_swipeBetweenPages() {
+        setUpTest(orientation).apply {
+            val expectedValues = stringSequence(totalPages)
+            val adapter = adapterProvider(expectedValues)
+
+            val fragmentManager = activity.supportFragmentManager
+
+            val transientStateCallback = createTransientStateCallback()
+            fragmentManager.registerFragmentLifecycleCallbacks(transientStateCallback, false)
+            setAdapterSync(adapter)
+
+            assertBasicState(0)
+            listOf(1, 0, 1, 2, 3, 4, 3).plus(4 until totalPages).forEach { target ->
+                val latch = viewPager.addWaitForIdleLatch()
+                swipe(viewPager.currentItem, target)
+                latch.await(timeoutMs, MILLISECONDS)
+                assertBasicState(target)
+            }
+
+            fragmentManager.unregisterFragmentLifecycleCallbacks(transientStateCallback)
+        }
+    }
+
+    private fun createTransientStateCallback(): FragmentManager.FragmentLifecycleCallbacks {
+        return object : FragmentManager.FragmentLifecycleCallbacks() {
+            override fun onFragmentViewCreated(
+                fm: FragmentManager,
+                f: Fragment,
+                v: View,
+                savedInstanceState: Bundle?
+            ) {
+                v.setHasTransientState(true)
+            }
+        }
+    }
+}
diff --git a/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java b/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
index 435762c..a07954e 100644
--- a/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
+++ b/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java
@@ -395,12 +395,20 @@
 
     @Override
     public final boolean onFailedToRecycleView(@NonNull FragmentViewHolder holder) {
-        // This happens when a ViewHolder is in a transient state (e.g. during custom
-        // animation). We don't have sufficient information on how to clear up what lead to
-        // the transient state, so we are throwing away the ViewHolder to stay on the
-        // conservative side.
-        onViewRecycled(holder); // the same clean-up steps as when recycling a ViewHolder
-        return false; // don't recycle the view
+        /*
+         This happens when a ViewHolder is in a transient state (e.g. during an
+         animation).
+
+         Our ViewHolders are effectively just FrameLayout instances in which we put Fragment
+         Views, so it's safe to force recycle them. This is because:
+         - FrameLayout instances are not to be directly manipulated, so no animations are
+         expected to be running directly on them.
+         - Fragment Views are not reused between position (one Fragment = one page). Animation
+         running in one of the Fragment Views won't affect another Fragment View.
+         - If a user chooses to violate these assumptions, they are also in the position to
+         correct the state in their code.
+        */
+        return true;
     }
 
     private void removeFragment(long itemId) {
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java b/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java
index 620ed4a..f5e9736 100644
--- a/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java
+++ b/viewpager2/src/main/java/androidx/viewpager2/widget/ScrollEventAdapter.java
@@ -379,9 +379,9 @@
      *
      * @return The current scroll position of the ViewPager, relative to its width
      */
-    float getRelativeScrollPosition() {
+    double getRelativeScrollPosition() {
         updateScrollEventValues();
-        return mScrollValues.mPosition + mScrollValues.mOffset;
+        return mScrollValues.mPosition + (double) mScrollValues.mOffset;
     }
 
     private void dispatchStateChanged(@ScrollState int state) {
diff --git a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
index 5f431bd..5b9f51e 100644
--- a/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
+++ b/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java
@@ -631,7 +631,7 @@
 
         // 2. Update the item internally
 
-        float previousItem = mCurrentItem;
+        double previousItem = mCurrentItem;
         mCurrentItem = item;
         mAccessibilityProvider.onSetNewCurrentItem();
 
@@ -895,9 +895,9 @@
         if (mPageTransformerAdapter.getPageTransformer() == null) {
             return;
         }
-        float relativePosition = mScrollEventAdapter.getRelativeScrollPosition();
+        double relativePosition = mScrollEventAdapter.getRelativeScrollPosition();
         int position = (int) relativePosition;
-        float positionOffset = relativePosition - position;
+        float positionOffset = (float) (relativePosition - position);
         int positionOffsetPx = Math.round(getPageSize() * positionOffset);
         mPageTransformerAdapter.onPageScrolled(position, positionOffset, positionOffsetPx);
     }
diff --git a/wear/api/1.1.0-alpha01.ignore b/wear/api/1.1.0-alpha01.ignore
deleted file mode 100644
index 6d5b030..0000000
--- a/wear/api/1.1.0-alpha01.ignore
+++ /dev/null
@@ -1,3 +0,0 @@
-// Baseline format: 1.0
-HiddenSuperclass: androidx.wear.widget.SwipeDismissFrameLayout:
-    Public class androidx.wear.widget.SwipeDismissFrameLayout stripped of unavailable superclass androidx.wear.widget.SwipeDismissLayout
diff --git a/webkit/api/1.1.0-alpha02.txt b/webkit/api/1.1.0-alpha02.txt
index 6ef8368..7242db2 100644
--- a/webkit/api/1.1.0-alpha02.txt
+++ b/webkit/api/1.1.0-alpha02.txt
@@ -1,8 +1,9 @@
 // Signature format: 3.0
 package androidx.webkit {
 
-  public class ProxyConfig {
-    field public static final String DIRECT = "direct://";
+  public final class ProxyConfig {
+    method public java.util.List<java.lang.String!> getBypassRules();
+    method public java.util.List<androidx.core.util.Pair<java.lang.String!,java.lang.String!>!> getProxyRules();
     field public static final String MATCH_ALL_SCHEMES = "*";
     field public static final String MATCH_HTTP = "http";
     field public static final String MATCH_HTTPS = "https";
@@ -12,11 +13,13 @@
     ctor public ProxyConfig.Builder();
     ctor public ProxyConfig.Builder(androidx.webkit.ProxyConfig);
     method public androidx.webkit.ProxyConfig.Builder addBypassRule(String);
+    method public androidx.webkit.ProxyConfig.Builder addDirect(String);
+    method public androidx.webkit.ProxyConfig.Builder addDirect();
     method public androidx.webkit.ProxyConfig.Builder addProxyRule(String);
     method public androidx.webkit.ProxyConfig.Builder addProxyRule(String, String);
     method public androidx.webkit.ProxyConfig build();
     method public androidx.webkit.ProxyConfig.Builder bypassSimpleHostnames();
-    method public androidx.webkit.ProxyConfig.Builder subtractImplicitRules();
+    method public androidx.webkit.ProxyConfig.Builder removeImplicitRules();
   }
 
   public abstract class ProxyController {
diff --git a/webkit/api/current.txt b/webkit/api/current.txt
index 6ef8368..7242db2 100644
--- a/webkit/api/current.txt
+++ b/webkit/api/current.txt
@@ -1,8 +1,9 @@
 // Signature format: 3.0
 package androidx.webkit {
 
-  public class ProxyConfig {
-    field public static final String DIRECT = "direct://";
+  public final class ProxyConfig {
+    method public java.util.List<java.lang.String!> getBypassRules();
+    method public java.util.List<androidx.core.util.Pair<java.lang.String!,java.lang.String!>!> getProxyRules();
     field public static final String MATCH_ALL_SCHEMES = "*";
     field public static final String MATCH_HTTP = "http";
     field public static final String MATCH_HTTPS = "https";
@@ -12,11 +13,13 @@
     ctor public ProxyConfig.Builder();
     ctor public ProxyConfig.Builder(androidx.webkit.ProxyConfig);
     method public androidx.webkit.ProxyConfig.Builder addBypassRule(String);
+    method public androidx.webkit.ProxyConfig.Builder addDirect(String);
+    method public androidx.webkit.ProxyConfig.Builder addDirect();
     method public androidx.webkit.ProxyConfig.Builder addProxyRule(String);
     method public androidx.webkit.ProxyConfig.Builder addProxyRule(String, String);
     method public androidx.webkit.ProxyConfig build();
     method public androidx.webkit.ProxyConfig.Builder bypassSimpleHostnames();
-    method public androidx.webkit.ProxyConfig.Builder subtractImplicitRules();
+    method public androidx.webkit.ProxyConfig.Builder removeImplicitRules();
   }
 
   public abstract class ProxyController {
diff --git a/webkit/build.gradle b/webkit/build.gradle
index 8c0c18d..622bf29 100644
--- a/webkit/build.gradle
+++ b/webkit/build.gradle
@@ -26,7 +26,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0-rc01")
+    api("androidx.core:core:1.1.0")
 
     androidTestImplementation(OKHTTP_MOCKWEBSERVER)
     androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/webkit/src/androidTest/java/androidx/webkit/ProxyControllerTest.java b/webkit/src/androidTest/java/androidx/webkit/ProxyControllerTest.java
index 994f0ae..e96423ef 100644
--- a/webkit/src/androidTest/java/androidx/webkit/ProxyControllerTest.java
+++ b/webkit/src/androidTest/java/androidx/webkit/ProxyControllerTest.java
@@ -104,7 +104,7 @@
         // Localhost should use proxy with loopback rule
         setProxyOverrideSync(new ProxyConfig.Builder()
                 .addProxyRule(proxyUrl)
-                .subtractImplicitRules()
+                .removeImplicitRules()
                 .build());
         mWebViewOnUiThread.loadUrl(contentUrl);
         assertNotNull(mProxyServer.takeRequest(WebkitUtils.TEST_TIMEOUT_MS,
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderIntegrationTest.java b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderIntegrationTest.java
index 70a2b11..06a7a6f 100644
--- a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderIntegrationTest.java
+++ b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderIntegrationTest.java
@@ -17,8 +17,10 @@
 package androidx.webkit;
 
 import static androidx.webkit.WebViewAssetLoader.AssetsPathHandler;
+import static androidx.webkit.WebViewAssetLoader.InternalStoragePathHandler;
 import static androidx.webkit.WebViewAssetLoader.ResourcesPathHandler;
 
+import android.content.Context;;
 import android.net.Uri;
 import android.webkit.WebResourceRequest;
 import android.webkit.WebResourceResponse;
@@ -27,6 +29,7 @@
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
+import androidx.webkit.internal.AssetHelper;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -35,6 +38,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.File;
+
 @RunWith(AndroidJUnit4.class)
 public class WebViewAssetLoaderIntegrationTest {
     private static final String TAG = "WebViewAssetLoaderIntegrationTest";
@@ -43,6 +48,12 @@
     public final ActivityTestRule<WebViewTestActivity> mActivityRule =
                                     new ActivityTestRule<>(WebViewTestActivity.class);
 
+    private static final String TEST_INTERNAL_STORAGE_DIR = "app_public/";
+    private static final String TEST_INTERNAL_STORAGE_FILE =
+            TEST_INTERNAL_STORAGE_DIR + "html/test_with_title.html";
+    private static final String TEST_HTML_CONTENT =
+            "<head><title>WebViewAssetLoaderTest</title></head>";
+
     private WebViewOnUiThread mOnUiThread;
 
     private static class AssetLoadingWebViewClient extends WebViewOnUiThread.WaitForLoadedClient {
@@ -76,6 +87,10 @@
         if (mOnUiThread != null) {
             mOnUiThread.cleanUp();
         }
+
+        Context context = mActivityRule.getActivity();
+        WebkitUtils.recursivelyDeleteFile(
+                new File(AssetHelper.getDataDir(context), TEST_INTERNAL_STORAGE_DIR));
     }
 
     @Test
@@ -125,4 +140,34 @@
 
         Assert.assertEquals("WebViewAssetLoaderTest", mOnUiThread.getTitle());
     }
+
+    @Test
+    @MediumTest
+    public void testAppInternalStorageHosting() throws Exception {
+        final WebViewTestActivity activity = mActivityRule.getActivity();
+
+        File dataDir = AssetHelper.getDataDir(activity);
+        WebkitUtils.writeToFile(new File(dataDir, TEST_INTERNAL_STORAGE_FILE), TEST_HTML_CONTENT);
+
+        InternalStoragePathHandler handler = new InternalStoragePathHandler(activity,
+                new File(dataDir, TEST_INTERNAL_STORAGE_DIR));
+        WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
+                .addPathHandler("/data/public/", handler)
+                .build();
+
+        mOnUiThread.setWebViewClient(new AssetLoadingWebViewClient(mOnUiThread, assetLoader));
+
+        String url = new Uri.Builder()
+                        .scheme("https")
+                        .authority(WebViewAssetLoader.DEFAULT_DOMAIN)
+                        .appendPath("data")
+                        .appendPath("public")
+                        .appendPath("html")
+                        .appendPath("test_with_title.html")
+                        .build()
+                        .toString();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+
+        Assert.assertEquals("WebViewAssetLoaderTest", mOnUiThread.getTitle());
+    }
 }
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java
index 029425a..f5a667b 100644
--- a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java
+++ b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java
@@ -16,11 +16,13 @@
 
 package androidx.webkit;
 
+import android.content.Context;
 import android.content.ContextWrapper;
 import android.net.Uri;
 import android.webkit.WebResourceResponse;
 
 import static androidx.webkit.WebViewAssetLoader.AssetsPathHandler;
+import static androidx.webkit.WebViewAssetLoader.InternalStoragePathHandler;
 import static androidx.webkit.WebViewAssetLoader.PathHandler;
 import static androidx.webkit.WebViewAssetLoader.ResourcesPathHandler;
 
@@ -37,6 +39,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
@@ -215,6 +218,84 @@
         assertResponse(response, testHtmlContents);
     }
 
+    @SmallTest
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateInternalStorageHandler_entireDataDir() throws Throwable {
+        Context context = ApplicationProvider.getApplicationContext();
+        File testDir = AssetHelper.getDataDir(context);
+        PathHandler handler =
+                new InternalStoragePathHandler(context, testDir);
+    }
+
+    @SmallTest
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateInternalStorageHandler_entireCacheDir() throws Throwable {
+        Context context = ApplicationProvider.getApplicationContext();
+        File testDir = context.getCacheDir();
+        PathHandler handler =
+                new InternalStoragePathHandler(context, testDir);
+    }
+
+    @SmallTest
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateInternalStorageHandler_databasesDir() throws Throwable {
+        Context context = ApplicationProvider.getApplicationContext();
+        File testDir = new File(AssetHelper.getDataDir(context), "databases/");
+        PathHandler handler =
+                new InternalStoragePathHandler(context, testDir);
+    }
+
+    @SmallTest
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateInternalStorageHandler_libDir() throws Throwable {
+        Context context = ApplicationProvider.getApplicationContext();
+        File testDir = new File(AssetHelper.getDataDir(context), "lib/");
+        PathHandler handler =
+                new InternalStoragePathHandler(context, testDir);
+    }
+
+    @SmallTest
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateInternalStorageHandler_webViewDir() throws Throwable {
+        Context context = ApplicationProvider.getApplicationContext();
+        File testDir = new File(AssetHelper.getDataDir(context), "app_webview");
+        PathHandler handler =
+                new InternalStoragePathHandler(context, testDir);
+    }
+
+    @SmallTest
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateInternalStorageHandler_sharedPrefsDir() throws Throwable {
+        Context context = ApplicationProvider.getApplicationContext();
+        File testDir = new File(AssetHelper.getDataDir(context), "/shared_prefs/");
+        PathHandler handler =
+                new InternalStoragePathHandler(context, testDir);
+    }
+
+    @Test
+    @SmallTest
+    public void testHostInternalStorageHandler_invalidAccess() throws Throwable {
+        Context context = ApplicationProvider.getApplicationContext();
+        File testDir = new File(AssetHelper.getDataDir(context), "/public/");
+        PathHandler handler =
+                new InternalStoragePathHandler(context, testDir);
+        WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
+                                                      .addPathHandler("/public-data/", handler)
+                                                      .build();
+
+        WebResourceResponse response = assetLoader.shouldInterceptRequest(
+                Uri.parse("https://appassets.androidplatform.net/public-data/../test.html"));
+        Assert.assertNull(
+                "should be null since it tries to access a file outside the mounted directory",
+                response.getData());
+
+        response = assetLoader.shouldInterceptRequest(
+                Uri.parse("https://appassets.androidplatform.net/public-data/html/test.html"));
+        Assert.assertNull(
+                "should be null as it accesses a non-existent file under the mounted directory",
+                response.getData());
+    }
+
     @Test
     @SmallTest
     public void testMultiplePathHandlers() throws Throwable {
@@ -314,6 +395,48 @@
         assertResponse(response, FakeTextPathHandler.CONTENTS);
     }
 
+    @Test
+    @SmallTest
+    public void testMimeTypeInPathHandlers() throws Throwable {
+        final String testHtmlContents = "<body><div>test</div></body>";
+
+        AssetHelper mockAssetHelper = new MockAssetHelper() {
+            @Override
+            public InputStream openResource(Uri uri) {
+                try {
+                    return new ByteArrayInputStream(testHtmlContents.getBytes(ENCODING));
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+
+        WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
+                .addPathHandler("/assets/", new AssetsPathHandler(mockAssetHelper))
+                .addPathHandler("/res/", new ResourcesPathHandler(mockAssetHelper))
+                .build();
+
+        WebResourceResponse response = assetLoader.shouldInterceptRequest(
+                Uri.parse("https://appassets.androidplatform.net/res/raw/test"));
+        Assert.assertEquals("File doesn't have an extension, MIME type should be text/plain",
+                AssetHelper.DEFAULT_MIME_TYPE, response.getMimeType());
+
+        response = assetLoader.shouldInterceptRequest(
+                Uri.parse("https://appassets.androidplatform.net/assets/other/test"));
+        Assert.assertEquals("File doesn't have an extension, MIME type should be text/plain",
+                AssetHelper.DEFAULT_MIME_TYPE, response.getMimeType());
+
+        response = assetLoader.shouldInterceptRequest(
+                Uri.parse("https://appassets.androidplatform.net/res/drawable/test.png"));
+        Assert.assertEquals(".png file should have mime type image/png regardless of its content",
+                "image/png", response.getMimeType());
+
+        response = assetLoader.shouldInterceptRequest(
+                Uri.parse("https://appassets.androidplatform.net/assets/images/test.png"));
+        Assert.assertEquals(".png file should have mime type image/png regardless of its content",
+                "image/png", response.getMimeType());
+    }
+
     private static void assertResponse(@Nullable WebResourceResponse response,
               @NonNull String expectedContent) throws IOException {
         Assert.assertNotNull("failed to match the URL and returned null response", response);
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebkitUtils.java b/webkit/src/androidTest/java/androidx/webkit/WebkitUtils.java
index 28d0633..a7994fc 100644
--- a/webkit/src/androidTest/java/androidx/webkit/WebkitUtils.java
+++ b/webkit/src/androidTest/java/androidx/webkit/WebkitUtils.java
@@ -25,6 +25,9 @@
 
 import org.junit.Assume;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
@@ -211,6 +214,42 @@
         }
     }
 
+    /**
+     * Write a string to a file, and create the whole parent directories if they don't exist.
+     */
+    public static void writeToFile(File file, String content)
+                  throws IOException {
+        file.getParentFile().mkdirs();
+        FileOutputStream fos = new FileOutputStream(file);
+        try {
+            fos.write(content.getBytes("utf-8"));
+        } finally {
+            fos.close();
+        }
+    }
+
+    /**
+     * Delete the given File and (if it's a directory) everything within it.
+     * @param currentFile The file or directory to delete. Does not need to exist.
+     * @return Whether currentFile does not exist afterwards.
+     */
+    public static boolean recursivelyDeleteFile(File currentFile) {
+        if (!currentFile.exists()) {
+            return true;
+        }
+        if (currentFile.isDirectory()) {
+            File[] files = currentFile.listFiles();
+            if (files != null) {
+                for (File file : files) {
+                    recursivelyDeleteFile(file);
+                }
+            }
+        }
+
+        boolean ret = currentFile.delete();
+        return ret;
+    }
+
     // Do not instantiate this class.
     private WebkitUtils() {}
 }
diff --git a/webkit/src/androidTest/java/androidx/webkit/internal/AssetHelperTest.java b/webkit/src/androidTest/java/androidx/webkit/internal/AssetHelperTest.java
index 9ecf3f0..5696e8a9 100644
--- a/webkit/src/androidTest/java/androidx/webkit/internal/AssetHelperTest.java
+++ b/webkit/src/androidTest/java/androidx/webkit/internal/AssetHelperTest.java
@@ -21,15 +21,19 @@
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
+import androidx.webkit.WebkitUtils;
 
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -39,11 +43,19 @@
 
     private static final String TEST_STRING = "Just a test";
     private AssetHelper mAssetHelper;
+    private File mInternalStorageTestDir;
 
     @Before
     public void setup() {
         Context context = InstrumentationRegistry.getContext();
         mAssetHelper = new AssetHelper(context);
+        mInternalStorageTestDir = new File(context.getFilesDir(), "test_dir");
+        mInternalStorageTestDir.mkdirs();
+    }
+
+    @After
+    public void tearDown() {
+        WebkitUtils.recursivelyDeleteFile(mInternalStorageTestDir);
     }
 
     @Test
@@ -114,6 +126,52 @@
                           mAssetHelper.openAsset(Uri.parse("/android_asset/test.txt")));
     }
 
+    @Test
+    @MediumTest
+    public void testOpenFileFromInternalStorage() throws Throwable {
+        File testFile = new File(mInternalStorageTestDir, "some_file.txt");
+        WebkitUtils.writeToFile(testFile, TEST_STRING);
+
+        InputStream stream = AssetHelper.openFile(testFile);
+        Assert.assertNotNull("Should be able to open \"" + testFile + "\" from internal storage",
+                stream);
+        Assert.assertEquals(readAsString(stream), TEST_STRING);
+    }
+
+    @Test
+    @MediumTest
+    public void testOpenNonExistingFileInInternalStorage() throws Throwable {
+        File testFile = new File(mInternalStorageTestDir, "some/path/to/non_exist_file.txt");
+        InputStream stream = AssetHelper.openFile(testFile);
+        Assert.assertNull("Should not be able to open a non existing file from internal storage",
+                stream);
+    }
+
+    @Test
+    @SmallTest
+    public void testIsCanonicalChildOf() throws Throwable {
+        // Two files are used for testing :
+        // "/some/path/to/file_1.txt" and "/some/path/file_2.txt"
+
+        File parent = new File(mInternalStorageTestDir, "/some/path/");
+        File child = new File(parent, "/to/./file_1.txt");
+        boolean res = AssetHelper.isCanonicalChildOf(parent, child);
+        Assert.assertTrue(
+                "/to/./\"file_1.txt\" is in a subdirectory of \"/some/path/\"", res);
+
+        parent = new File(mInternalStorageTestDir, "/some/path/");
+        child = new File(parent, "/to/../file_2.txt");
+        res = AssetHelper.isCanonicalChildOf(parent, child);
+        Assert.assertTrue(
+                "/to/../\"file_2.txt\" is in a subdirectory of \"/some/path/\"", res);
+
+        parent = new File(mInternalStorageTestDir, "/some/path/to");
+        child = new File(parent, "/../file_2.txt");
+        res = AssetHelper.isCanonicalChildOf(parent, child);
+        Assert.assertFalse(
+                "/../\"file_2.txt\" is not in a subdirectory of \"/some/path/to/\"", res);
+    }
+
     private static String readAsString(InputStream is) {
         ByteArrayOutputStream os = new ByteArrayOutputStream();
         byte[] buffer = new byte[512];
diff --git a/webkit/src/main/java/androidx/webkit/ProxyConfig.java b/webkit/src/main/java/androidx/webkit/ProxyConfig.java
index 4491f3c..74adb9c 100644
--- a/webkit/src/main/java/androidx/webkit/ProxyConfig.java
+++ b/webkit/src/main/java/androidx/webkit/ProxyConfig.java
@@ -19,6 +19,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.StringDef;
+import androidx.core.util.Pair;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -39,15 +40,11 @@
  * <pre class="prettyprint">
  * ProxyConfig proxyConfig = new ProxyConfig.Builder().addProxyRule("proxy1.com")
  *                                                    .addProxyRule("proxy2.com")
- *                                                    .addProxyRule(ProxyConfig.DIRECT)
+ *                                                    .addDirect()
  *                                                    .build();
  * </pre>
  */
-public class ProxyConfig {
-    /**
-     * Connect to URLs directly instead of using a proxy server.
-     */
-    public static final String DIRECT = "direct://";
+public final class ProxyConfig {
     /**
      * HTTP scheme.
      */
@@ -65,36 +62,46 @@
     @StringDef({MATCH_HTTP, MATCH_HTTPS, MATCH_ALL_SCHEMES})
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProxyScheme {}
+    private static final String DIRECT = "direct://";
     private static final String BYPASS_RULE_SIMPLE_NAMES = "<local>";
-    private static final String BYPASS_RULE_SUBTRACT_IMPLICIT = "<-loopback>";
+    private static final String BYPASS_RULE_REMOVE_IMPLICIT = "<-loopback>";
 
-    private List<String[]> mProxyRules;
+    private List<Pair<String, String>> mProxyRules;
     private List<String> mBypassRules;
 
     /**
      * @hide Internal use only
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public ProxyConfig(List<String[]> proxyRules, List<String> bypassRules) {
+    public ProxyConfig(List<Pair<String, String>> proxyRules, List<String> bypassRules) {
         mProxyRules = proxyRules;
         mBypassRules = bypassRules;
     }
 
     /**
-     * @hide Internal use only
+     * Returns the current list of proxy rules. Each pair of (String, String) consists of the proxy
+     * URL and the URL schemes for which this proxy is used (one of {@code MATCH_HTTP},
+     * {@code MATCH_HTTPS}, {@code MATCH_ALL_SCHEMES}).
+     *
+     * <p>To add new rules use {@link Builder#addProxyRule(String)} or
+     * {@link Builder#addProxyRule(String, String)}.
+     *
+     * @return List of proxy rules
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
     @NonNull
-    public List<String[]> proxyRules() {
+    public List<Pair<String, String>> getProxyRules() {
         return mProxyRules;
     }
 
     /**
-     * @hide Internal use only
+     * Returns the current list that holds the bypass rules represented by this object.
+     *
+     * <p>To add new rules use {@link Builder#addBypassRule(String)}.
+     *
+     * @return List of bypass rules
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
     @NonNull
-    public List<String> bypassRules() {
+    public List<String> getBypassRules() {
         return mBypassRules;
     }
 
@@ -108,7 +115,7 @@
      * connections to be made directly.
      */
     public static final class Builder {
-        private List<String[]> mProxyRules;
+        private List<Pair<String, String>> mProxyRules;
         private List<String> mBypassRules;
 
         /**
@@ -123,12 +130,14 @@
          * Create a ProxyConfig Builder from an existing ProxyConfig object.
          */
         public Builder(@NonNull ProxyConfig proxyConfig) {
-            mProxyRules = proxyConfig.proxyRules();
-            mBypassRules = proxyConfig.bypassRules();
+            mProxyRules = proxyConfig.getProxyRules();
+            mBypassRules = proxyConfig.getBypassRules();
         }
 
         /**
          * Builds the current rules into a ProxyConfig object.
+         *
+         * @return The ProxyConfig object represented by this Builder
          */
         @NonNull
         public ProxyConfig build() {
@@ -136,12 +145,13 @@
         }
 
         /**
-         * Adds a proxy to be used for all URLs.
-         * <p>Proxy is either {@link ProxyConfig#DIRECT} or a string in the format
-         * {@code [scheme://]host[:port]}. Scheme is optional, if present must be {@code HTTP},
-         * {@code HTTPS} or <a href="https://tools.ietf.org/html/rfc1928">SOCKS</a> and defaults to
-         * {@code HTTP}. Host is one of an IPv6 literal with brackets, an IPv4 literal or one or
-         * more labels separated by a period. Port number is optional and defaults to {@code 80} for
+         * Adds a proxy to be used for all URLs. This method can be called multiple times to add
+         * multiple rules. Additional rules have decreasing precedence.
+         * <p>Proxy is a string in the format {@code [scheme://]host[:port]}. Scheme is optional, if
+         * present must be {@code HTTP}, {@code HTTPS} or
+         * <a href="https://tools.ietf.org/html/rfc1928">SOCKS</a> and defaults to {@code HTTP}.
+         * Host is one of an IPv6 literal with brackets, an IPv4 literal or one or more labels
+         * separated by a period. Port number is optional and defaults to {@code 80} for
          * {@code HTTP}, {@code 443} for {@code HTTPS} and {@code 1080} for {@code SOCKS}.
          * <p>
          * The correct syntax for hosts is defined by
@@ -161,6 +171,7 @@
          * </table>
          *
          * @param proxyUrl Proxy URL
+         * @return This Builder object
          */
         @NonNull
         public Builder addProxyRule(@NonNull String proxyUrl) {
@@ -175,23 +186,24 @@
          *
          * @param proxyUrl Proxy URL
          * @param schemeFilter Scheme filter
+         * @return This Builder object
          */
         @NonNull
         public Builder addProxyRule(@NonNull String proxyUrl,
                 @NonNull @ProxyScheme String schemeFilter) {
-            String[] rule = {schemeFilter, proxyUrl};
-            mProxyRules.add(rule);
+            mProxyRules.add(new Pair<>(schemeFilter, proxyUrl));
             return this;
         }
 
         /**
          * Adds a new bypass rule that describes URLs that should skip proxy override settings
-         * and make a direct connection instead. Wildcards are accepted. For instance, the rule
-         * {@code "*example.com"} would mean that requests to {@code "http://example.com"} and
-         * {@code "www.example.com"} would not be directed to any proxy, instead, would be made
-         * directly to the origin specified by the URL.
+         * and make a direct connection instead. These can be URLs or IP addresses. Wildcards are
+         * accepted. For instance, the rule {@code "*example.com"} would mean that requests to
+         * {@code "http://example.com"} and {@code "www.example.com"} would not be directed to any
+         * proxy, instead, would be made directly to the origin specified by the URL.
          *
          * @param bypassRule Rule to be added to the exclusion list
+         * @return This Builder object
          */
         @NonNull
         public Builder addBypassRule(@NonNull String bypassRule) {
@@ -200,11 +212,36 @@
         }
 
         /**
+         * Adds a proxy rule so URLs that match the scheme filter are connected to directly instead
+         * of using a proxy server.
+         *
+         * @param schemeFilter Scheme filter
+         * @return This Builder object
+         */
+        @NonNull
+        public Builder addDirect(@NonNull @ProxyScheme String schemeFilter) {
+            mProxyRules.add(new Pair<>(DIRECT, schemeFilter));
+            return this;
+        }
+
+        /**
+         * Adds a proxy rule so URLs are connected to directly instead of using a proxy server.
+         *
+         * @return This Builder object
+         */
+        @NonNull
+        public Builder addDirect() {
+            return addDirect(MATCH_ALL_SCHEMES);
+        }
+
+        /**
          * Hostnames without a period in them (and that are not IP literals) will skip proxy
          * settings and be connected to directly instead. Examples: {@code "abc"}, {@code "local"},
          * {@code "some-domain"}.
          * <p>
          * Hostnames with a trailing dot are not considered simple by this definition.
+         *
+         * @return This Builder object
          */
         @NonNull
         public Builder bypassSimpleHostnames() {
@@ -225,14 +262,16 @@
          * <p>
          * Call this function to override the default behavior and force localhost and link-local
          * URLs to be sent through the proxy.
+         *
+         * @return This Builder object
          */
         @NonNull
-        public Builder subtractImplicitRules() {
-            return addBypassRule(BYPASS_RULE_SUBTRACT_IMPLICIT);
+        public Builder removeImplicitRules() {
+            return addBypassRule(BYPASS_RULE_REMOVE_IMPLICIT);
         }
 
         @NonNull
-        private List<String[]> proxyRules() {
+        private List<Pair<String, String>> proxyRules() {
             return mProxyRules;
         }
 
diff --git a/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java b/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
index fa064fc..b34d682 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
@@ -18,24 +18,28 @@
 
 import android.content.Context;
 import android.net.Uri;
+import android.util.Log;
 import android.webkit.WebResourceResponse;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
 import androidx.annotation.VisibleForTesting;
 import androidx.annotation.WorkerThread;
 import androidx.webkit.internal.AssetHelper;
 
+import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
-import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- * Helper class to load files including application's static assets and resources using http(s)://
- * URLs inside a {@link android.webkit.WebView} class.
- * Loading assets and resources using web-like URLs is desirable as it is compatible with the
- * Same-Origin policy.
+ * Helper class to load local files including application's static assets and resources using
+ * http(s):// URLs inside a {@link android.webkit.WebView} class.
+ * Loading local files using web-like URLs instead of {@code "file://"} is desirable as it is
+ * compatible with the Same-Origin policy.
  *
  * <p>
  * For more context about application's assets and resources and how to normally access them please
@@ -144,6 +148,11 @@
          * falling back to network and trying to resolve a path that doesn't exist. A
          * {@link WebResourceResponse} with {@code null} {@link InputStream} will be received as an
          * HTTP response with status code {@code 404} and no body.
+         * <p class="note">
+         * The MIME type for the file will be determined from the file's extension using
+         * {@link java.net.URLConnection#guessContentTypeFromName}. Developers should ensure that
+         * asset files are named using standard file extensions. If the file does not have a
+         * recognised extension, {@code "text/plain"} will be used by default.
          *
          * @param path the suffix path to be handled.
          * @return {@link WebResourceResponse} for the requested file.
@@ -157,7 +166,7 @@
                     .build();
 
             InputStream is = mAssetHelper.openAsset(uri);
-            String mimeType = URLConnection.guessContentTypeFromName(path);
+            String mimeType = AssetHelper.guessMimeType(path);
             return new WebResourceResponse(mimeType, null, is);
         }
     }
@@ -189,6 +198,11 @@
          * falling back to network and trying to resolve a path that doesn't exist. A
          * {@link WebResourceResponse} with {@code null} {@link InputStream} will be received as an
          * HTTP response with status code {@code 404} and no body.
+         * <p class="note">
+         * The MIME type for the file will be determined from the file's extension using
+         * {@link java.net.URLConnection#guessContentTypeFromName}. Developers should ensure that
+         * resource files are named using standard file extensions. If the file does not have a
+         * recognised extension, {@code "text/plain"} will be used by default.
          *
          * @param path the suffix path to be handled.
          * @return {@link WebResourceResponse} for the requested file.
@@ -202,13 +216,125 @@
                     .build();
 
             InputStream is = mAssetHelper.openResource(uri);
-            String mimeType = URLConnection.guessContentTypeFromName(path);
+            String mimeType = AssetHelper.guessMimeType(path);
             return new WebResourceResponse(mimeType, null, is);
         }
 
     }
 
     /**
+     * Handler class to open files from application internal storage.
+     * For more information about android storage please refer to
+     * <a href="https://developer.android.com/guide/topics/data/data-storage">Android Developers
+     * Docs: Data and file storage overview</a>.
+     * <p>
+     * To avoid leaking user or app data to the web, make sure to choose {@code directory}
+     * carefully, and assume any file under this directory could be accessed by any web page subject
+     * to same-origin rules.
+     * @hide
+     */
+    // TODO(b/132880733) unhide the API when it's ready.
+    @RestrictTo(Scope.LIBRARY_GROUP_PREFIX)
+    public static final class InternalStoragePathHandler implements PathHandler {
+        /**
+         * Forbidden subdirectories of {@link Context#getDataDir} that cannot be exposed by this
+         * handler. They are forbidden as they often contain sensitive information.
+         */
+        public static final String[] FORBIDDEN_DATA_DIRS =
+                new String[] {"app_webview/", "databases/", "lib/", "shared_prefs/", "code_cache/"};
+
+        @NonNull private final File mDirectory;
+
+        /**
+         * Creates PathHandler for app's internal storage.
+         * The directory to be exposed must be inside either the application's internal data
+         * directory {@link context#getDataDir} or cache directory {@link context#getCacheDir}.
+         * External storage is not supported for security reasons, as other apps with
+         * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} may be able to modify the
+         * files.
+         * <p>
+         * Exposing the entire data or cache directory is not permitted, to avoid accidentally
+         * exposing sensitive application files to the web. Certain existing directories are also
+         * not permitted, such as {@link FORBIDDEN_DATA_DIRS}, as they are often sensitive.
+         * <p>
+         * The application should typically use a dedicated subdirectory for the files it intends to
+         * expose and keep them separate from other files.
+         *
+         * @param context {@link Context} that is used to access app's internal storage.
+         * @param directory the absolute path of the exposed app internal storage directory from
+         *                  which files can be loaded.
+         * @throws IllegalArgumentException if the directory is not allowed.
+         */
+        public InternalStoragePathHandler(@NonNull Context context, @NonNull File directory) {
+            if (!isAllowedInternalStorageDir(context, directory)) {
+                throw new IllegalArgumentException("The given directory \"" + directory
+                        + "\" doesn't exist under an allowed app internal storage directory");
+            }
+            mDirectory = directory;
+        }
+
+        private static boolean isAllowedInternalStorageDir(@NonNull Context context,
+                @NonNull File dir) {
+            try {
+                String dirPath = AssetHelper.getCanonicalPath(dir);
+                String cacheDirPath = AssetHelper.getCanonicalPath(context.getCacheDir());
+                String dataDirPath = AssetHelper.getCanonicalPath(AssetHelper.getDataDir(context));
+                // dir has to be a subdirectory of data or cache dir.
+                if (!dirPath.startsWith(cacheDirPath) && !dirPath.startsWith(dataDirPath)) {
+                    return false;
+                }
+                // dir cannot be the entire cache or data dir.
+                if (dirPath.equals(cacheDirPath) || dirPath.equals(dataDirPath)) {
+                    return false;
+                }
+                // dir cannot be a subdirectory of any forbidden data dir.
+                for (String forbiddenPath : FORBIDDEN_DATA_DIRS) {
+                    if (dirPath.startsWith(dataDirPath + forbiddenPath)) return false;
+                }
+                return true;
+            } catch (IOException e) {
+                return false;
+            }
+        }
+
+        /**
+         * Opens the requested file from the exposed data directory.
+         * <p>
+         * The matched prefix path used shouldn't be a prefix of a real web path. Thus, if the
+         * requested file cannot be found or is outside the mounted directory a
+         * {@link WebResourceResponse} object with a {@code null} {@link InputStream} will be
+         * returned instead of {@code null}. This saves the time of falling back to network and
+         * trying to resolve a path that doesn't exist. A {@link WebResourceResponse} with
+         * {@code null} {@link InputStream} will be received as an HTTP response with status code
+         * {@code 404} and no body.
+         * <p class="note">
+         * The MIME type for the file will be determined from the file's extension using
+         * {@link java.net.URLConnection#guessContentTypeFromName}. Developers should ensure that
+         * files are named using standard file extensions. If the file does not have a
+         * recognised extension, {@code "text/plain"} will be used by default.
+         *
+         * @param path the suffix path to be handled.
+         * @return {@link WebResourceResponse} for the requested file.
+         */
+        @Override
+        @WorkerThread
+        @NonNull
+        public WebResourceResponse handle(@NonNull String path) {
+            File file = new File(mDirectory, path);
+            InputStream is = null;
+            if (AssetHelper.isCanonicalChildOf(mDirectory, file)) {
+                is = AssetHelper.openFile(file);
+            } else {
+                Log.e(TAG, "The requested file: " + path + " is outside the mounted directory: "
+                         + mDirectory);
+            }
+            String mimeType = AssetHelper.guessMimeType(path);
+            return new WebResourceResponse(mimeType, null, is);
+        }
+    }
+
+
+    /**
      * Matches URIs on the form: {@code "http(s)://authority/path/**"}, HTTPS is always enabled.
      *
      * <p>
diff --git a/webkit/src/main/java/androidx/webkit/internal/AssetHelper.java b/webkit/src/main/java/androidx/webkit/internal/AssetHelper.java
index d2f2dfe..e6d5d2e 100644
--- a/webkit/src/main/java/androidx/webkit/internal/AssetHelper.java
+++ b/webkit/src/main/java/androidx/webkit/internal/AssetHelper.java
@@ -20,14 +20,18 @@
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.net.Uri;
+import android.os.Build;
 import android.util.Log;
 import android.util.TypedValue;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URLConnection;
 import java.util.List;
 import java.util.zip.GZIPInputStream;
 
@@ -38,6 +42,11 @@
 public class AssetHelper {
     private static final String TAG = "AssetHelper";
 
+    /**
+     * Default value to be used as MIME type if guessing MIME type failed.
+     */
+    public static final String DEFAULT_MIME_TYPE = "text/plain";
+
     @NonNull private Context mContext;
 
     public AssetHelper(@NonNull Context context) {
@@ -124,4 +133,85 @@
             return null;
         }
     }
+
+    /**
+     * Open an {@code InputStream} for a file in application data directories.
+     *
+     * @param file The the file to be opened.
+     * @return An {@code InputStream} for the requested file or {@code null} if an error happens.
+     */
+    @Nullable
+    public static InputStream openFile(@NonNull File file) {
+        try {
+            FileInputStream fis = new FileInputStream(file);
+            return handleSvgzStream(Uri.parse(file.getPath()), fis);
+        } catch (IOException e) {
+            Log.e(TAG, "Error opening the requested file " + file, e);
+            return null;
+        }
+    }
+
+    /**
+     * Util method to test if the a given file is a child of the given parent directory.
+     * It uses canonical paths to make sure to resolve any symlinks, {@code "../"}, {@code "./"}
+     * ... etc in the given paths.
+     *
+     * @param parent the parent directory.
+     * @param child the child file.
+     * @return {@code true} if the canonical path of the given {@code child} starts with the
+     *         canonical path of the given {@code parent}, {@code false} otherwise.
+     */
+    public static boolean isCanonicalChildOf(@NonNull File parent, @NonNull File child) {
+        try {
+            String parentCanonicalPath = parent.getCanonicalPath();
+            String childCanonicalPath = child.getCanonicalPath();
+
+            if (!parentCanonicalPath.endsWith("/")) parentCanonicalPath += "/";
+
+            return childCanonicalPath.startsWith(parentCanonicalPath);
+        } catch (IOException e) {
+            Log.e(TAG, "Error getting the canonical path of file", e);
+            return false;
+        }
+    }
+
+    /**
+     * Get the canonical path of the given directory with a slash {@code "/"} at the end.
+     */
+    @NonNull
+    public static String getCanonicalPath(@NonNull File file) throws IOException {
+        String path = file.getCanonicalPath();
+        if (!path.endsWith("/")) path += "/";
+        return path;
+    }
+
+    /**
+     * Get the data dir for an application.
+     *
+     * @param context the {@link Context} used to get the data dir.
+     * @return data dir {@link File} for that app.
+     */
+    @NonNull
+    public static File getDataDir(@NonNull Context context) {
+        // Context#getDataDir is only available in APIs >= 24.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            return context.getDataDir();
+        } else {
+            // For APIs < 24 cache dir is created under the data dir.
+            return context.getCacheDir().getParentFile();
+        }
+    }
+
+    /**
+     * Use {@link URLConnection#guessContentTypeFromName} to guess MIME type or return the
+     * {@link DEFAULT_MIME_TYPE} if it can't guess.
+     *
+     * @param filePath path of the file to guess its MIME type.
+     * @return MIME type guessed from file extension or {@link DEFAULT_MIME_TYPE}.
+     */
+    @NonNull
+    public static String guessMimeType(@NonNull String filePath) {
+        String mimeType = URLConnection.guessContentTypeFromName(filePath);
+        return mimeType == null ? DEFAULT_MIME_TYPE : mimeType;
+    }
 }
diff --git a/webkit/src/main/java/androidx/webkit/internal/ProxyControllerImpl.java b/webkit/src/main/java/androidx/webkit/internal/ProxyControllerImpl.java
index 1b61f33..c2b4675 100644
--- a/webkit/src/main/java/androidx/webkit/internal/ProxyControllerImpl.java
+++ b/webkit/src/main/java/androidx/webkit/internal/ProxyControllerImpl.java
@@ -17,12 +17,14 @@
 package androidx.webkit.internal;
 
 import androidx.annotation.NonNull;
+import androidx.core.util.Pair;
 import androidx.webkit.ProxyConfig;
 import androidx.webkit.ProxyController;
 import androidx.webkit.WebViewFeature;
 
 import org.chromium.support_lib_boundary.ProxyControllerBoundaryInterface;
 
+import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
@@ -37,9 +39,16 @@
         WebViewFeatureInternal webViewFeature =
                 WebViewFeatureInternal.getFeature(WebViewFeature.PROXY_OVERRIDE);
         if (webViewFeature.isSupportedByWebView()) {
-            getBoundaryInterface().setProxyOverride(
-                    proxyConfig.proxyRules().toArray(new String[0][]),
-                    proxyConfig.bypassRules().toArray(new String[0]), listener, executor);
+            List<Pair<String, String>> proxyRulesList = proxyConfig.getProxyRules();
+
+            // A 2D String array representation is required by reflection
+            String[][] proxyRulesArray = new String[proxyRulesList.size()][2];
+            for (int i = 0; i < proxyRulesList.size(); i++) {
+                proxyRulesArray[i][0] = proxyRulesList.get(0).first;
+                proxyRulesArray[i][1] = proxyRulesList.get(0).second;
+            }
+            getBoundaryInterface().setProxyOverride(proxyRulesArray,
+                    proxyConfig.getBypassRules().toArray(new String[0]), listener, executor);
         } else {
             throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
diff --git a/work/workmanager-gcm/src/androidTest/java/androidx/work/impl/background/gcm/GcmTaskConverterTest.kt b/work/workmanager-gcm/src/androidTest/java/androidx/work/impl/background/gcm/GcmTaskConverterTest.kt
index 547c3ac..89a0e4d 100644
--- a/work/workmanager-gcm/src/androidTest/java/androidx/work/impl/background/gcm/GcmTaskConverterTest.kt
+++ b/work/workmanager-gcm/src/androidTest/java/androidx/work/impl/background/gcm/GcmTaskConverterTest.kt
@@ -58,16 +58,17 @@
 
         val expected = request.workSpec.calculateNextRunTime()
         val offset = offset(expected, now)
-        val delta = task.windowEnd - (offset + EXECUTION_WINDOW_SIZE_IN_SECONDS)
+        val deltaStart = task.windowStart - offset
+        val deltaEnd = task.windowEnd - (offset + EXECUTION_WINDOW_SIZE_IN_SECONDS)
 
         assertEquals(task.serviceName, WorkManagerGcmService::class.java.name)
         assertEquals(task.isPersisted, false)
         assertEquals(task.isUpdateCurrent, true)
         assertEquals(task.requiredNetwork, Task.NETWORK_STATE_ANY)
         assertEquals(task.requiresCharging, false)
-        assertEquals(task.windowStart, offset)
         // Account for time unit quantization errors
-        assertThat(delta, lessThanOrEqualTo(1L))
+        assertThat(deltaStart, lessThanOrEqualTo(1L))
+        assertThat(deltaEnd, lessThanOrEqualTo(1L))
     }
 
     @Test
@@ -87,16 +88,17 @@
         val task = mTaskConverter.convert(request.workSpec)
         val expected = request.workSpec.calculateNextRunTime()
         val offset = offset(expected, now)
-        val delta = task.windowEnd - (offset + EXECUTION_WINDOW_SIZE_IN_SECONDS)
+        val deltaStart = task.windowStart - offset
+        val deltaEnd = task.windowEnd - (offset + EXECUTION_WINDOW_SIZE_IN_SECONDS)
 
         assertEquals(task.serviceName, WorkManagerGcmService::class.java.name)
         assertEquals(task.isPersisted, false)
         assertEquals(task.isUpdateCurrent, true)
         assertEquals(task.requiredNetwork, Task.NETWORK_STATE_CONNECTED)
         assertEquals(task.requiresCharging, true)
-        assertEquals(task.windowStart, offset)
         // Account for time unit quantization errors
-        assertThat(delta, lessThanOrEqualTo(1L))
+        assertThat(deltaStart, lessThanOrEqualTo(1L))
+        assertThat(deltaEnd, lessThanOrEqualTo(1L))
     }
 
     @Test
@@ -115,16 +117,17 @@
         val task = mTaskConverter.convert(request.workSpec)
         val expected = request.workSpec.calculateNextRunTime()
         val offset = offset(expected, now)
-        val delta = task.windowEnd - (offset + EXECUTION_WINDOW_SIZE_IN_SECONDS)
+        val deltaStart = task.windowStart - offset
+        val deltaEnd = task.windowEnd - (offset + EXECUTION_WINDOW_SIZE_IN_SECONDS)
 
         assertEquals(task.serviceName, WorkManagerGcmService::class.java.name)
         assertEquals(task.isPersisted, false)
         assertEquals(task.isUpdateCurrent, true)
         assertEquals(task.requiredNetwork, Task.NETWORK_STATE_UNMETERED)
         assertEquals(task.requiresCharging, false)
-        assertEquals(task.windowStart, offset)
         // Account for time unit quantization errors
-        assertThat(delta, lessThanOrEqualTo(1L))
+        assertThat(deltaStart, lessThanOrEqualTo(1L))
+        assertThat(deltaEnd, lessThanOrEqualTo(1L))
     }
 
     @Test
@@ -140,16 +143,17 @@
         val task = mTaskConverter.convert(request.workSpec)
         val expected = request.workSpec.calculateNextRunTime()
         val offset = offset(expected, now)
-        val delta = task.windowEnd - (offset + EXECUTION_WINDOW_SIZE_IN_SECONDS)
+        val deltaStart = task.windowStart - offset
+        val deltaEnd = task.windowEnd - (offset + EXECUTION_WINDOW_SIZE_IN_SECONDS)
 
         assertEquals(task.serviceName, WorkManagerGcmService::class.java.name)
         assertEquals(task.isPersisted, false)
         assertEquals(task.isUpdateCurrent, true)
         assertEquals(task.requiredNetwork, Task.NETWORK_STATE_ANY)
         assertEquals(task.requiresCharging, false)
-        assertEquals(task.windowStart, offset)
         // Account for time unit quantization errors
-        assertThat(delta, lessThanOrEqualTo(1L))
+        assertThat(deltaStart, lessThanOrEqualTo(1L))
+        assertThat(deltaEnd, lessThanOrEqualTo(1L))
     }
 
     @Test
@@ -165,16 +169,17 @@
         val task = mTaskConverter.convert(request.workSpec)
         val expected = workSpec.calculateNextRunTime()
         val offset = offset(expected, now)
-        val delta = task.windowEnd - (offset + EXECUTION_WINDOW_SIZE_IN_SECONDS)
+        val deltaStart = task.windowStart - offset
+        val deltaEnd = task.windowEnd - (offset + EXECUTION_WINDOW_SIZE_IN_SECONDS)
 
         assertEquals(task.serviceName, WorkManagerGcmService::class.java.name)
         assertEquals(task.isPersisted, false)
         assertEquals(task.isUpdateCurrent, true)
         assertEquals(task.requiredNetwork, Task.NETWORK_STATE_ANY)
         assertEquals(task.requiresCharging, false)
-        assertEquals(task.windowStart, offset)
         // Account for time unit quantization errors
-        assertThat(delta, lessThanOrEqualTo(1L))
+        assertThat(deltaStart, lessThanOrEqualTo(1L))
+        assertThat(deltaEnd, lessThanOrEqualTo(1L))
     }
 
     @Test
@@ -189,16 +194,17 @@
         val task = mTaskConverter.convert(request.workSpec)
         val expected = request.workSpec.calculateNextRunTime()
         val offset = offset(expected, now)
-        val delta = task.windowEnd - (offset + EXECUTION_WINDOW_SIZE_IN_SECONDS)
+        val deltaStart = task.windowStart - offset
+        val deltaEnd = task.windowEnd - (offset + EXECUTION_WINDOW_SIZE_IN_SECONDS)
 
         assertEquals(task.serviceName, WorkManagerGcmService::class.java.name)
         assertEquals(task.isPersisted, false)
         assertEquals(task.isUpdateCurrent, true)
         assertEquals(task.requiredNetwork, Task.NETWORK_STATE_ANY)
         assertEquals(task.requiresCharging, false)
-        assertEquals(task.windowStart, offset)
         // Account for time unit quantization errors
-        assertThat(delta, lessThanOrEqualTo(1L))
+        assertThat(deltaStart, lessThanOrEqualTo(1L))
+        assertThat(deltaEnd, lessThanOrEqualTo(1L))
     }
 
     @Test
diff --git a/work/workmanager-gcm/src/androidTest/java/androidx/work/impl/background/gcm/WorkManagerGcmDispatcherTest.kt b/work/workmanager-gcm/src/androidTest/java/androidx/work/impl/background/gcm/WorkManagerGcmDispatcherTest.kt
index c67dfd2..f127aba 100644
--- a/work/workmanager-gcm/src/androidTest/java/androidx/work/impl/background/gcm/WorkManagerGcmDispatcherTest.kt
+++ b/work/workmanager-gcm/src/androidTest/java/androidx/work/impl/background/gcm/WorkManagerGcmDispatcherTest.kt
@@ -25,6 +25,7 @@
 import androidx.test.filters.MediumTest
 import androidx.work.Configuration
 import androidx.work.impl.WorkManagerImpl
+import androidx.work.impl.utils.SerialExecutor
 import androidx.work.impl.utils.SynchronousExecutor
 import com.google.android.gms.gcm.GcmNetworkManager
 import com.google.android.gms.gcm.TaskParams
@@ -63,7 +64,8 @@
 
         val workTaskExecutor: androidx.work.impl.utils.taskexecutor.TaskExecutor =
             object : androidx.work.impl.utils.taskexecutor.TaskExecutor {
-                override fun postToMainThread(runnable: Runnable?) {
+                private val mSerialExecutor = SerialExecutor(mExecutor)
+                override fun postToMainThread(runnable: Runnable) {
                     mExecutor.execute(runnable)
                 }
 
@@ -71,12 +73,12 @@
                     return mExecutor
                 }
 
-                override fun executeOnBackgroundThread(runnable: Runnable?) {
-                    mExecutor.execute(runnable)
+                override fun executeOnBackgroundThread(runnable: Runnable) {
+                    mSerialExecutor.execute(runnable)
                 }
 
-                override fun getBackgroundExecutor(): Executor {
-                    return mExecutor
+                override fun getBackgroundExecutor(): SerialExecutor {
+                    return mSerialExecutor
                 }
             }
 
diff --git a/work/workmanager-ktx/api/2.3.0-alpha01.txt b/work/workmanager-ktx/api/2.3.0-alpha01.txt
index 4ed72f8..ddfb009 100644
--- a/work/workmanager-ktx/api/2.3.0-alpha01.txt
+++ b/work/workmanager-ktx/api/2.3.0-alpha01.txt
@@ -6,6 +6,7 @@
     method public abstract suspend Object doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
     method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
     method public final void onStopped();
+    method public final suspend Object! setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
     property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
   }
diff --git a/work/workmanager-ktx/api/current.txt b/work/workmanager-ktx/api/current.txt
index 4ed72f8..ddfb009 100644
--- a/work/workmanager-ktx/api/current.txt
+++ b/work/workmanager-ktx/api/current.txt
@@ -6,6 +6,7 @@
     method public abstract suspend Object doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
     method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
     method public final void onStopped();
+    method public final suspend Object! setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
     property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
   }
diff --git a/work/workmanager-ktx/api/restricted_2.3.0-alpha01.txt b/work/workmanager-ktx/api/restricted_2.3.0-alpha01.txt
index 2a2fd52..5b6423e 100644
--- a/work/workmanager-ktx/api/restricted_2.3.0-alpha01.txt
+++ b/work/workmanager-ktx/api/restricted_2.3.0-alpha01.txt
@@ -6,6 +6,7 @@
     method public abstract suspend Object doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
     method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
     method public final void onStopped();
+    method public final suspend Object! setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
     property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
   }
diff --git a/work/workmanager-ktx/api/restricted_current.txt b/work/workmanager-ktx/api/restricted_current.txt
index 2a2fd52..5b6423e 100644
--- a/work/workmanager-ktx/api/restricted_current.txt
+++ b/work/workmanager-ktx/api/restricted_current.txt
@@ -6,6 +6,7 @@
     method public abstract suspend Object doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
     method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
     method public final void onStopped();
+    method public final suspend Object! setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
     method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
     property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
   }
diff --git a/work/workmanager-ktx/build.gradle b/work/workmanager-ktx/build.gradle
index 93c1a0e..0cee793 100644
--- a/work/workmanager-ktx/build.gradle
+++ b/work/workmanager-ktx/build.gradle
@@ -53,6 +53,9 @@
     androidTestImplementation(ANDROIDX_TEST_CORE)
     androidTestImplementation(ANDROIDX_TEST_RUNNER)
     androidTestImplementation(ESPRESSO_CORE)
+    androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has its own MockMaker
+    androidTestImplementation(WORK_ARCH_ROOM_TESTING)
     testImplementation(JUNIT)
 }
 
diff --git a/work/workmanager-ktx/src/androidTest/java/androidx/work/CoroutineWorkerTest.kt b/work/workmanager-ktx/src/androidTest/java/androidx/work/CoroutineWorkerTest.kt
index 6232be6..301e7e1 100644
--- a/work/workmanager-ktx/src/androidTest/java/androidx/work/CoroutineWorkerTest.kt
+++ b/work/workmanager-ktx/src/androidTest/java/androidx/work/CoroutineWorkerTest.kt
@@ -19,15 +19,19 @@
 import android.content.Context
 import android.util.Log
 import androidx.arch.core.executor.ArchTaskExecutor
-
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
 import androidx.test.filters.SmallTest
 import androidx.work.impl.WorkDatabase
 import androidx.work.impl.WorkManagerImpl
+import androidx.work.impl.utils.SerialExecutor
+import androidx.work.impl.utils.WorkProgressUpdater
 import androidx.work.impl.utils.futures.SettableFuture
 import androidx.work.impl.utils.taskexecutor.TaskExecutor
+import androidx.work.workers.ProgressUpdatingWorker
 import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.runBlocking
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.instanceOf
 import org.hamcrest.MatcherAssert.assertThat
@@ -35,6 +39,11 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
 import java.util.UUID
 import java.util.concurrent.Executor
 
@@ -65,7 +74,7 @@
                 }
             })
 
-        context = ApplicationProvider.getApplicationContext() as android.content.Context
+        context = ApplicationProvider.getApplicationContext()
         configuration = Configuration.Builder()
             .setExecutor(SynchronousExecutor())
             .setMinimumLoggingLevel(Log.DEBUG)
@@ -141,6 +150,44 @@
         assertThat(worker.job.isCancelled, `is`(true))
     }
 
+    @Test
+    @LargeTest
+    fun testProgressUpdates() {
+        val workerFactory = WorkerFactory.getDefaultWorkerFactory()
+        val progressUpdater = spy(WorkProgressUpdater(database, workManagerImpl.workTaskExecutor))
+        val workRequest = OneTimeWorkRequestBuilder<ProgressUpdatingWorker>().build()
+        database.workSpecDao().insertWorkSpec(workRequest.workSpec)
+        val worker = workerFactory.createWorkerWithDefaultFallback(
+            context,
+            ProgressUpdatingWorker::class.java.name,
+            WorkerParameters(
+                workRequest.id,
+                Data.EMPTY,
+                emptyList(),
+                WorkerParameters.RuntimeExtras(),
+                1,
+                configuration.executor,
+                workManagerImpl.workTaskExecutor,
+                workerFactory,
+                progressUpdater
+            )
+        ) as ProgressUpdatingWorker
+
+        runBlocking {
+            val result = worker.doWork()
+            val captor = ArgumentCaptor.forClass(Data::class.java)
+            verify(progressUpdater, times(2))
+                .updateProgress(
+                    any(Context::class.java),
+                    any(UUID::class.java),
+                    captor.capture()
+                )
+            assertThat(result, `is`(instanceOf(ListenableWorker.Result.Success::class.java)))
+            val recent = captor.allValues.lastOrNull()
+            assertThat(recent?.getInt(ProgressUpdatingWorker.Progress, 0), `is`(100))
+        }
+    }
+
     class SynchronousExecutor : Executor {
 
         override fun execute(command: Runnable) {
@@ -151,6 +198,7 @@
     class InstantWorkTaskExecutor : TaskExecutor {
 
         private val mSynchronousExecutor = SynchronousExecutor()
+        private val mSerialExecutor = SerialExecutor(mSynchronousExecutor)
 
         override fun postToMainThread(runnable: Runnable) {
             runnable.run()
@@ -161,11 +209,11 @@
         }
 
         override fun executeOnBackgroundThread(runnable: Runnable) {
-            runnable.run()
+            mSerialExecutor.execute(runnable)
         }
 
-        override fun getBackgroundExecutor(): Executor {
-            return mSynchronousExecutor
+        override fun getBackgroundExecutor(): SerialExecutor {
+            return mSerialExecutor
         }
     }
 
diff --git a/work/workmanager-ktx/src/androidTest/java/androidx/work/workers/ProgressUpdatingWorker.kt b/work/workmanager-ktx/src/androidTest/java/androidx/work/workers/ProgressUpdatingWorker.kt
new file mode 100644
index 0000000..f6d65ba
--- /dev/null
+++ b/work/workmanager-ktx/src/androidTest/java/androidx/work/workers/ProgressUpdatingWorker.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.work.workers
+
+import android.content.Context
+import androidx.work.CoroutineWorker
+import androidx.work.Data
+import androidx.work.WorkerParameters
+import kotlinx.coroutines.delay
+
+class ProgressUpdatingWorker(context: Context, parameters: WorkerParameters) :
+    CoroutineWorker(context, parameters) {
+
+    companion object {
+        const val Progress = "Progress"
+        private const val delayDuration = 1L
+    }
+
+    override suspend fun doWork(): Result {
+        val firstUpdate = Data.Builder().putInt(Progress, 10).build()
+        val lastUpdate = Data.Builder().putInt(Progress, 100).build()
+        setProgress(firstUpdate)
+        delay(delayDuration)
+        setProgress(lastUpdate)
+        return Result.success()
+    }
+}
\ No newline at end of file
diff --git a/work/workmanager-ktx/src/main/java/androidx/work/CoroutineWorker.kt b/work/workmanager-ktx/src/main/java/androidx/work/CoroutineWorker.kt
index 56e5679..c1d12bb 100644
--- a/work/workmanager-ktx/src/main/java/androidx/work/CoroutineWorker.kt
+++ b/work/workmanager-ktx/src/main/java/androidx/work/CoroutineWorker.kt
@@ -88,6 +88,16 @@
      */
     abstract suspend fun doWork(): Result
 
+    /**
+     * Updates the progress for the [CoroutineWorker]. This is a suspending function unlike the
+     * [setProgressAsync] API which returns a [ListenableFuture].
+     *
+     * @param data The progress [Data]
+     */
+    suspend fun setProgress(data: Data) {
+        setProgressAsync(data).await()
+    }
+
     final override fun onStopped() {
         super.onStopped()
         future.cancel(false)
diff --git a/work/workmanager-rxjava2/api/2.3.0-alpha01.txt b/work/workmanager-rxjava2/api/2.3.0-alpha01.txt
index e43f0e5..757ca4c 100644
--- a/work/workmanager-rxjava2/api/2.3.0-alpha01.txt
+++ b/work/workmanager-rxjava2/api/2.3.0-alpha01.txt
@@ -5,6 +5,7 @@
     ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
     method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
     method protected io.reactivex.Scheduler getBackgroundScheduler();
+    method public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
diff --git a/work/workmanager-rxjava2/api/current.txt b/work/workmanager-rxjava2/api/current.txt
index e43f0e5..757ca4c 100644
--- a/work/workmanager-rxjava2/api/current.txt
+++ b/work/workmanager-rxjava2/api/current.txt
@@ -5,6 +5,7 @@
     ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
     method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
     method protected io.reactivex.Scheduler getBackgroundScheduler();
+    method public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
diff --git a/work/workmanager-rxjava2/api/restricted_2.3.0-alpha01.txt b/work/workmanager-rxjava2/api/restricted_2.3.0-alpha01.txt
index e43f0e5..757ca4c 100644
--- a/work/workmanager-rxjava2/api/restricted_2.3.0-alpha01.txt
+++ b/work/workmanager-rxjava2/api/restricted_2.3.0-alpha01.txt
@@ -5,6 +5,7 @@
     ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
     method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
     method protected io.reactivex.Scheduler getBackgroundScheduler();
+    method public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
diff --git a/work/workmanager-rxjava2/api/restricted_current.txt b/work/workmanager-rxjava2/api/restricted_current.txt
index e43f0e5..757ca4c 100644
--- a/work/workmanager-rxjava2/api/restricted_current.txt
+++ b/work/workmanager-rxjava2/api/restricted_current.txt
@@ -5,6 +5,7 @@
     ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
     method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
     method protected io.reactivex.Scheduler getBackgroundScheduler();
+    method public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
     method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
diff --git a/work/workmanager-rxjava2/src/main/java/androidx/work/RxWorker.java b/work/workmanager-rxjava2/src/main/java/androidx/work/RxWorker.java
index de7fcf0..2200863 100644
--- a/work/workmanager-rxjava2/src/main/java/androidx/work/RxWorker.java
+++ b/work/workmanager-rxjava2/src/main/java/androidx/work/RxWorker.java
@@ -117,10 +117,22 @@
     @MainThread
     public abstract @NonNull Single<Result> createWork();
 
+    /**
+     * Updates the progress for a {@link RxWorker}. This method returns a {@link Single} unlike the
+     * {@link ListenableWorker#setProgressAsync(Data)} API.
+     *
+     * @param data The progress {@link Data}
+     * @return The {@link Single}
+     */
+    @NonNull
+    public final Single<Void> setProgress(@NonNull Data data) {
+        return Single.fromFuture(setProgressAsync(data));
+    }
+
     @Override
     public void onStopped() {
         super.onStopped();
-        final SingleFutureAdapter observer = mSingleFutureObserverAdapter;
+        final SingleFutureAdapter<Result> observer = mSingleFutureObserverAdapter;
         if (observer != null) {
             observer.dispose();
             mSingleFutureObserverAdapter = null;
diff --git a/work/workmanager-rxjava2/src/test/java/androidx/work/RxWorkerTest.kt b/work/workmanager-rxjava2/src/test/java/androidx/work/RxWorkerTest.kt
index bf07147..2e7c0a0 100644
--- a/work/workmanager-rxjava2/src/test/java/androidx/work/RxWorkerTest.kt
+++ b/work/workmanager-rxjava2/src/test/java/androidx/work/RxWorkerTest.kt
@@ -17,6 +17,7 @@
 package androidx.work
 
 import android.content.Context
+import androidx.work.impl.utils.SerialExecutor
 import androidx.work.impl.utils.SynchronousExecutor
 import androidx.work.impl.utils.taskexecutor.TaskExecutor
 import io.reactivex.Single
@@ -140,6 +141,7 @@
     class InstantWorkTaskExecutor : TaskExecutor {
 
         private val mSynchronousExecutor = SynchronousExecutor()
+        private val mSerialExecutor = SerialExecutor(mSynchronousExecutor)
 
         override fun postToMainThread(runnable: Runnable) {
             runnable.run()
@@ -150,11 +152,11 @@
         }
 
         override fun executeOnBackgroundThread(runnable: Runnable) {
-            runnable.run()
+            mSerialExecutor.execute(runnable)
         }
 
-        override fun getBackgroundExecutor(): Executor {
-            return mSynchronousExecutor
+        override fun getBackgroundExecutor(): SerialExecutor {
+            return mSerialExecutor
         }
     }
 }
diff --git a/work/workmanager-testing/src/androidTest/java/androidx/work/testing/WorkManagerInitHelperTest.java b/work/workmanager-testing/src/androidTest/java/androidx/work/testing/WorkManagerInitHelperTest.java
index d089045..32f1a86 100644
--- a/work/workmanager-testing/src/androidTest/java/androidx/work/testing/WorkManagerInitHelperTest.java
+++ b/work/workmanager-testing/src/androidTest/java/androidx/work/testing/WorkManagerInitHelperTest.java
@@ -28,6 +28,7 @@
 import androidx.work.Configuration;
 import androidx.work.WorkManager;
 import androidx.work.impl.WorkManagerImpl;
+import androidx.work.impl.utils.SerialExecutor;
 
 import org.junit.After;
 import org.junit.Before;
@@ -60,11 +61,13 @@
     public void testWorkManagerIsInitialized() {
         Configuration configuration = new Configuration.Builder()
                 .setExecutor(mExecutor)
+                .setTaskExecutor(mExecutor)
                 .build();
 
         WorkManagerTestInitHelper.initializeTestWorkManager(mContext, configuration);
         WorkManagerImpl workManager = (WorkManagerImpl) WorkManager.getInstance(mContext);
         assertThat(workManager, is(notNullValue()));
-        assertThat(workManager.getWorkTaskExecutor().getBackgroundExecutor(), is(mExecutor));
+        SerialExecutor serialExecutor = workManager.getWorkTaskExecutor().getBackgroundExecutor();
+        assertThat(serialExecutor.getDelegatedExecutor(), is(mExecutor));
     }
 }
diff --git a/work/workmanager-testing/src/main/java/androidx/work/testing/InstantWorkTaskExecutor.java b/work/workmanager-testing/src/main/java/androidx/work/testing/InstantWorkTaskExecutor.java
index f727fd7..36e7764 100644
--- a/work/workmanager-testing/src/main/java/androidx/work/testing/InstantWorkTaskExecutor.java
+++ b/work/workmanager-testing/src/main/java/androidx/work/testing/InstantWorkTaskExecutor.java
@@ -17,6 +17,7 @@
 package androidx.work.testing;
 
 import androidx.annotation.NonNull;
+import androidx.work.impl.utils.SerialExecutor;
 import androidx.work.impl.utils.SynchronousExecutor;
 import androidx.work.impl.utils.taskexecutor.TaskExecutor;
 
@@ -28,6 +29,7 @@
 class InstantWorkTaskExecutor implements TaskExecutor {
 
     private Executor mSynchronousExecutor = new SynchronousExecutor();
+    private SerialExecutor mSerialExecutor = new SerialExecutor(mSynchronousExecutor);
 
     @NonNull
     Executor getSynchronousExecutor() {
@@ -46,11 +48,11 @@
 
     @Override
     public void executeOnBackgroundThread(Runnable runnable) {
-        runnable.run();
+        mSerialExecutor.execute(runnable);
     }
 
     @Override
-    public Executor getBackgroundExecutor() {
-        return mSynchronousExecutor;
+    public SerialExecutor getBackgroundExecutor() {
+        return mSerialExecutor;
     }
 }
diff --git a/work/workmanager-testing/src/main/java/androidx/work/testing/TestWorkManagerImpl.java b/work/workmanager-testing/src/main/java/androidx/work/testing/TestWorkManagerImpl.java
index f956bdb..4e7c916 100644
--- a/work/workmanager-testing/src/main/java/androidx/work/testing/TestWorkManagerImpl.java
+++ b/work/workmanager-testing/src/main/java/androidx/work/testing/TestWorkManagerImpl.java
@@ -54,7 +54,8 @@
                 configuration,
                 new TaskExecutor() {
                     Executor mSynchronousExecutor = new SynchronousExecutor();
-                    Executor mBackgroundExecutor = new SerialExecutor(configuration.getExecutor());
+                    SerialExecutor mSerialExecutor =
+                            new SerialExecutor(configuration.getTaskExecutor());
 
                     @Override
                     public void postToMainThread(Runnable runnable) {
@@ -68,12 +69,12 @@
 
                     @Override
                     public void executeOnBackgroundThread(Runnable runnable) {
-                        mBackgroundExecutor.execute(runnable);
+                        mSerialExecutor.execute(runnable);
                     }
 
                     @Override
-                    public Executor getBackgroundExecutor() {
-                        return configuration.getExecutor();
+                    public SerialExecutor getBackgroundExecutor() {
+                        return mSerialExecutor;
                     }
                 },
                 true);
diff --git a/work/workmanager-testing/src/main/java/androidx/work/testing/WorkManagerTestInitHelper.java b/work/workmanager-testing/src/main/java/androidx/work/testing/WorkManagerTestInitHelper.java
index f0e3c34..1864168 100644
--- a/work/workmanager-testing/src/main/java/androidx/work/testing/WorkManagerTestInitHelper.java
+++ b/work/workmanager-testing/src/main/java/androidx/work/testing/WorkManagerTestInitHelper.java
@@ -37,6 +37,7 @@
         SynchronousExecutor synchronousExecutor = new SynchronousExecutor();
         Configuration configuration = new Configuration.Builder()
                 .setExecutor(synchronousExecutor)
+                .setTaskExecutor(synchronousExecutor)
                 .build();
         initializeTestWorkManager(context, configuration);
     }
diff --git a/work/workmanager/api/2.3.0-alpha01.txt b/work/workmanager/api/2.3.0-alpha01.txt
index 3656be3..a6cb786 100644
--- a/work/workmanager/api/2.3.0-alpha01.txt
+++ b/work/workmanager/api/2.3.0-alpha01.txt
@@ -143,7 +143,7 @@
     method @RequiresApi(24) public final java.util.List<android.net.Uri!> getTriggeredContentUris();
     method public final boolean isStopped();
     method public void onStopped();
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setProgress(androidx.work.Data);
+    method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setProgressAsync(androidx.work.Data);
     method @MainThread public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
diff --git a/work/workmanager/api/current.txt b/work/workmanager/api/current.txt
index 3656be3..a6cb786 100644
--- a/work/workmanager/api/current.txt
+++ b/work/workmanager/api/current.txt
@@ -143,7 +143,7 @@
     method @RequiresApi(24) public final java.util.List<android.net.Uri!> getTriggeredContentUris();
     method public final boolean isStopped();
     method public void onStopped();
-    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setProgress(androidx.work.Data);
+    method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setProgressAsync(androidx.work.Data);
     method @MainThread public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
   }
 
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/background/greedy/GreedySchedulerTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/background/greedy/GreedySchedulerTest.java
index 32d6c33..99d2a83 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/background/greedy/GreedySchedulerTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/background/greedy/GreedySchedulerTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.Mockito.when;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
 import androidx.test.filters.SmallTest;
 import androidx.work.Constraints;
 import androidx.work.OneTimeWorkRequest;
@@ -107,6 +108,20 @@
 
     @Test
     @SmallTest
+    @SdkSuppress(minSdkVersion = 23)
+    public void testGreedyScheduler_ignoresIdleWorkConstraint() {
+        Constraints constraints = new Constraints.Builder()
+                .setRequiresDeviceIdle(true)
+                .build();
+        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class)
+                .setConstraints(constraints)
+                .build();
+        mGreedyScheduler.schedule(getWorkSpec(work));
+        verify(mMockWorkConstraintsTracker, never()).replace(ArgumentMatchers.<WorkSpec>anyList());
+    }
+
+    @Test
+    @SmallTest
     public void testGreedyScheduler_startsWorkWhenConstraintsMet() {
         mGreedyScheduler.onAllConstraintsMet(Collections.singletonList(TEST_ID));
         verify(mWorkManagerImpl).startWork(TEST_ID);
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/utils/taskexecutor/InstantWorkTaskExecutor.java b/work/workmanager/src/androidTest/java/androidx/work/impl/utils/taskexecutor/InstantWorkTaskExecutor.java
index 8bfee8e..965f9b3 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/utils/taskexecutor/InstantWorkTaskExecutor.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/utils/taskexecutor/InstantWorkTaskExecutor.java
@@ -16,6 +16,7 @@
 
 package androidx.work.impl.utils.taskexecutor;
 
+import androidx.work.impl.utils.SerialExecutor;
 import androidx.work.impl.utils.SynchronousExecutor;
 
 import java.util.concurrent.Executor;
@@ -26,6 +27,7 @@
 public class InstantWorkTaskExecutor implements TaskExecutor {
 
     private Executor mSynchronousExecutor = new SynchronousExecutor();
+    private SerialExecutor mBackgroundExecutor = new SerialExecutor(mSynchronousExecutor);
 
     @Override
     public void postToMainThread(Runnable runnable) {
@@ -43,7 +45,7 @@
     }
 
     @Override
-    public Executor getBackgroundExecutor() {
-        return mSynchronousExecutor;
+    public SerialExecutor getBackgroundExecutor() {
+        return mBackgroundExecutor;
     }
 }
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/workers/ConstraintTrackingWorkerTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/workers/ConstraintTrackingWorkerTest.java
index f897235..8469605 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/workers/ConstraintTrackingWorkerTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/workers/ConstraintTrackingWorkerTest.java
@@ -30,8 +30,8 @@
 import androidx.annotation.NonNull;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
 import androidx.test.filters.SdkSuppress;
-import androidx.test.filters.SmallTest;
 import androidx.work.Configuration;
 import androidx.work.Constraints;
 import androidx.work.Data;
@@ -69,7 +69,7 @@
 import java.util.concurrent.Executors;
 
 @RunWith(AndroidJUnit4.class)
-@SmallTest
+@LargeTest
 public class ConstraintTrackingWorkerTest extends DatabaseTest {
 
     private static final long DELAY_IN_MS = 100;
diff --git a/work/workmanager/src/main/java/androidx/work/ListenableWorker.java b/work/workmanager/src/main/java/androidx/work/ListenableWorker.java
index 72a7d5d..2203618 100644
--- a/work/workmanager/src/main/java/androidx/work/ListenableWorker.java
+++ b/work/workmanager/src/main/java/androidx/work/ListenableWorker.java
@@ -195,7 +195,7 @@
      * Cancelling this future is a no-op.
      */
     @NonNull
-    public ListenableFuture<Void> setProgress(@NonNull Data data) {
+    public final ListenableFuture<Void> setProgressAsync(@NonNull Data data) {
         return mWorkerParams.getProgressUpdater()
                 .updateProgress(getApplicationContext(), getId(), data);
     }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java b/work/workmanager/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java
index c2d0e4f..c54c6f9 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/greedy/GreedyScheduler.java
@@ -16,8 +16,9 @@
 
 package androidx.work.impl.background.greedy;
 
+import static android.os.Build.VERSION.SDK_INT;
+
 import android.content.Context;
-import android.os.Build;
 import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
@@ -87,10 +88,17 @@
                     && workSpec.initialDelay == 0L
                     && !workSpec.isBackedOff()) {
                 if (workSpec.hasConstraints()) {
-                    // Exclude content URI triggers - we don't know how to handle them here so the
-                    // background scheduler should take care of them.
-                    if (Build.VERSION.SDK_INT < 24
-                            || !workSpec.constraints.hasContentUriTriggers()) {
+                    if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
+                        // Ignore requests that have an idle mode constraint.
+                        Logger.get().debug(TAG,
+                                String.format("Ignoring WorkSpec %s, Requires device idle.",
+                                        workSpec));
+                    } else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
+                        // Ignore requests that have content uri triggers.
+                        Logger.get().debug(TAG,
+                                String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.",
+                                        workSpec));
+                    } else {
                         constrainedWorkSpecs.add(workSpec);
                         constrainedWorkSpecIds.add(workSpec.id);
                     }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcher.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcher.java
index 548da36..5ddb288 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcher.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/SystemAlarmDispatcher.java
@@ -32,6 +32,7 @@
 import androidx.work.impl.ExecutionListener;
 import androidx.work.impl.Processor;
 import androidx.work.impl.WorkManagerImpl;
+import androidx.work.impl.utils.SerialExecutor;
 import androidx.work.impl.utils.WakeLocks;
 import androidx.work.impl.utils.taskexecutor.TaskExecutor;
 
@@ -221,8 +222,11 @@
                 }
                 mCurrentIntent = null;
             }
+            SerialExecutor serialExecutor = mTaskExecutor.getBackgroundExecutor();
+            if (!mCommandHandler.hasPendingCommands()
+                    && mIntents.isEmpty()
+                    && !serialExecutor.hasPendingTasks()) {
 
-            if (!mCommandHandler.hasPendingCommands() && mIntents.isEmpty()) {
                 // If there are no more intents to process, and the command handler
                 // has no more pending commands, stop the service.
                 Logger.get().debug(TAG, "No more commands & intents.");
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java
index d4de7b9..03df2de 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java
@@ -264,9 +264,7 @@
             List<JobInfo> jobs = getPendingJobs(context, jobScheduler);
             if (jobs != null && !jobs.isEmpty()) {
                 for (JobInfo jobInfo : jobs) {
-                    PersistableBundle extras = jobInfo.getExtras();
-                    //noinspection ConstantConditions
-                    if (extras == null || !extras.containsKey(EXTRA_WORK_SPEC_ID)) {
+                    if (getWorkSpecIdFromJobInfo(jobInfo) == null) {
                         cancelJobById(jobScheduler, jobInfo.getId());
                     }
                 }
@@ -312,7 +310,6 @@
      * For reference: b/133556574, b/133556809, b/133556535
      */
     @Nullable
-    @SuppressWarnings("ConstantConditions")
     private static List<Integer> getPendingJobIds(
             @NonNull Context context,
             @NonNull JobScheduler jobScheduler,
@@ -327,14 +324,25 @@
         List<Integer> jobIds = new ArrayList<>(2);
 
         for (JobInfo jobInfo : jobs) {
-            PersistableBundle extras = jobInfo.getExtras();
-            if (extras != null && extras.containsKey(EXTRA_WORK_SPEC_ID)) {
-                if (workSpecId.equals(extras.getString(EXTRA_WORK_SPEC_ID))) {
-                    jobIds.add(jobInfo.getId());
-                }
+            if (workSpecId.equals(getWorkSpecIdFromJobInfo(jobInfo))) {
+                jobIds.add(jobInfo.getId());
             }
         }
 
         return jobIds;
     }
-}
+
+    @SuppressWarnings("ConstantConditions")
+    private static @Nullable String getWorkSpecIdFromJobInfo(@NonNull JobInfo jobInfo) {
+        PersistableBundle extras = jobInfo.getExtras();
+        try {
+            if (extras != null && extras.containsKey(EXTRA_WORK_SPEC_ID)) {
+                return extras.getString(EXTRA_WORK_SPEC_ID);
+            }
+        } catch (NullPointerException e) {
+            // b/138364061: BaseBundle.mMap seems to be null in some cases here.  Ignore and return
+            // null.
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobService.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobService.java
index aa28fb2..bbc87a0 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobService.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobService.java
@@ -16,6 +16,8 @@
 
 package androidx.work.impl.background.systemjob;
 
+import static androidx.work.impl.background.systemjob.SystemJobInfoConverter.EXTRA_WORK_SPEC_ID;
+
 import android.app.Application;
 import android.app.job.JobParameters;
 import android.app.job.JobService;
@@ -24,6 +26,7 @@
 import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.work.Logger;
@@ -86,22 +89,14 @@
     }
 
     @Override
-    public boolean onStartJob(JobParameters params) {
+    public boolean onStartJob(@NonNull JobParameters params) {
         if (mWorkManagerImpl == null) {
             Logger.get().debug(TAG, "WorkManager is not initialized; requesting retry.");
             jobFinished(params, true);
             return false;
         }
 
-        PersistableBundle extras = params.getExtras();
-        // This can be null, possibly on a device-specific/API-specific (23) situation.  b/134028277
-        //noinspection ConstantConditions
-        if (extras == null) {
-            Logger.get().error(TAG, "No extras in JobParameters.");
-            return false;
-        }
-
-        String workSpecId = extras.getString(SystemJobInfoConverter.EXTRA_WORK_SPEC_ID);
+        String workSpecId = getWorkSpecIdFromJobParameters(params);
         if (TextUtils.isEmpty(workSpecId)) {
             Logger.get().error(TAG, "WorkSpec id not found!");
             return false;
@@ -152,21 +147,13 @@
     }
 
     @Override
-    public boolean onStopJob(JobParameters params) {
+    public boolean onStopJob(@NonNull JobParameters params) {
         if (mWorkManagerImpl == null) {
             Logger.get().debug(TAG, "WorkManager is not initialized; requesting retry.");
             return true;
         }
 
-        PersistableBundle extras = params.getExtras();
-        // This can be null, possibly on a device-specific/API-specific (23) situation.  b/134028277
-        //noinspection ConstantConditions
-        if (extras == null) {
-            Logger.get().error(TAG, "No extras in JobParameters.");
-            return false;
-        }
-
-        String workSpecId = extras.getString(SystemJobInfoConverter.EXTRA_WORK_SPEC_ID);
+        String workSpecId = getWorkSpecIdFromJobParameters(params);
         if (TextUtils.isEmpty(workSpecId)) {
             Logger.get().error(TAG, "WorkSpec id not found!");
             return false;
@@ -192,4 +179,18 @@
             jobFinished(parameters, needsReschedule);
         }
     }
+
+    @Nullable
+    @SuppressWarnings("ConstantConditions")
+    private static String getWorkSpecIdFromJobParameters(@NonNull JobParameters parameters) {
+        try {
+            PersistableBundle extras = parameters.getExtras();
+            if (extras != null && extras.containsKey(EXTRA_WORK_SPEC_ID)) {
+                return extras.getString(EXTRA_WORK_SPEC_ID);
+            }
+        } catch (NullPointerException e) {
+            // b/138441699: BaseBundle.getString sometimes throws an NPE.  Ignore and return null.
+        }
+        return null;
+    }
 }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/SerialExecutor.java b/work/workmanager/src/main/java/androidx/work/impl/utils/SerialExecutor.java
index 90e9adc..982c865 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/SerialExecutor.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/SerialExecutor.java
@@ -17,6 +17,7 @@
 package androidx.work.impl.utils;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 
 import java.util.ArrayDeque;
 import java.util.concurrent.Executor;
@@ -57,6 +58,21 @@
     }
 
     /**
+     * @return {@code true} if there are tasks to execute in the queue.
+     */
+    public boolean hasPendingTasks() {
+        synchronized (mLock) {
+            return !mTasks.isEmpty();
+        }
+    }
+
+    @NonNull
+    @VisibleForTesting
+    public Executor getDelegatedExecutor() {
+        return mExecutor;
+    }
+
+    /**
      * A {@link Runnable} which tells the {@link SerialExecutor} to schedule the next command
      * after completion.
      */
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/WorkProgressUpdater.java b/work/workmanager/src/main/java/androidx/work/impl/utils/WorkProgressUpdater.java
index 38cd978..dc114ee 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/WorkProgressUpdater.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/WorkProgressUpdater.java
@@ -73,13 +73,14 @@
                     if (state == null) {
                         Logger.get().warning(TAG,
                                 String.format(
-                                        "Ignoring setProgress(...). WorkSpec (%s) does not exist.",
+                                        "Ignoring setProgressAsync(...). WorkSpec (%s) does not "
+                                                + "exist.",
                                         workSpecId));
                     } else if (state.isFinished()) {
                         Logger.get().warning(TAG,
                                 String.format(
-                                        "Ignoring setProgress(...). WorkSpec (%s) has finished "
-                                                + "execution.",
+                                        "Ignoring setProgressAsync(...). WorkSpec (%s) has "
+                                                + "finished execution.",
                                         workSpecId));
                     } else {
                         WorkProgress progress = new WorkProgress(workSpecId, data);
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/taskexecutor/TaskExecutor.java b/work/workmanager/src/main/java/androidx/work/impl/utils/taskexecutor/TaskExecutor.java
index e5df2d9..2542e40 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/taskexecutor/TaskExecutor.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/taskexecutor/TaskExecutor.java
@@ -17,6 +17,7 @@
 package androidx.work.impl.utils.taskexecutor;
 
 import androidx.annotation.RestrictTo;
+import androidx.work.impl.utils.SerialExecutor;
 
 import java.util.concurrent.Executor;
 
@@ -44,7 +45,7 @@
     void executeOnBackgroundThread(Runnable runnable);
 
     /**
-     * @return The {@link Executor} for background task processing
+     * @return The {@link SerialExecutor} for background task processing
      */
-    Executor getBackgroundExecutor();
+    SerialExecutor getBackgroundExecutor();
 }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/taskexecutor/WorkManagerTaskExecutor.java b/work/workmanager/src/main/java/androidx/work/impl/utils/taskexecutor/WorkManagerTaskExecutor.java
index d689d89..6717838 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/taskexecutor/WorkManagerTaskExecutor.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/taskexecutor/WorkManagerTaskExecutor.java
@@ -32,7 +32,7 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class WorkManagerTaskExecutor implements TaskExecutor {
 
-    private final Executor mBackgroundExecutor;
+    private final SerialExecutor mBackgroundExecutor;
 
     public WorkManagerTaskExecutor(@NonNull Executor backgroundExecutor) {
         // Wrap it with a serial executor so we have ordering guarantees on commands
@@ -65,7 +65,8 @@
     }
 
     @Override
-    public Executor getBackgroundExecutor() {
+    @NonNull
+    public SerialExecutor getBackgroundExecutor() {
         return mBackgroundExecutor;
     }
 }