[go: nahoru, domu]

Merge "PreTest camera for extension tests" into androidx-master-dev
diff --git a/appcompat/appcompat/src/androidTest/AndroidManifest.xml b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
index 9d3d665..9af9433 100644
--- a/appcompat/appcompat/src/androidTest/AndroidManifest.xml
+++ b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
@@ -125,6 +125,11 @@
             android:theme="@style/Theme.AppCompat.Light"/>
 
         <activity
+            android:name="androidx.appcompat.widget.AppCompatIconsActivity"
+            android:label="@string/app_compat_icons_activity"
+            android:theme="@style/Theme.AppCompat.Light"/>
+
+        <activity
             android:name="androidx.appcompat.widget.AppCompatRadioButtonActivity"
             android:label="@string/app_compat_radio_button_activity"
             android:theme="@style/Theme.AppCompat.Light"/>
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatIconsActivity.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatIconsActivity.java
new file mode 100644
index 0000000..5e1f331
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatIconsActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.appcompat.widget;
+
+import androidx.appcompat.test.R;
+import androidx.appcompat.testutils.BaseTestActivity;
+
+public class AppCompatIconsActivity extends BaseTestActivity {
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.appcompat_icons_activity;
+    }
+}
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatIconsTest.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatIconsTest.kt
new file mode 100644
index 0000000..37a1584
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatIconsTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appcompat.widget
+
+import android.util.TypedValue
+import androidx.appcompat.content.res.AppCompatResources
+import androidx.appcompat.test.R
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.junit.Assert.assertNotNull
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+class AppCompatIconsTest {
+    @get:Rule
+    val activityRule = ActivityScenarioRule(AppCompatIconsActivity::class.java)
+
+    private lateinit var activityScenario: ActivityScenario<AppCompatIconsActivity>
+
+    @Before
+    fun setup() {
+        activityScenario = ActivityScenario.launch(AppCompatIconsActivity::class.java)
+    }
+
+    @Test
+    fun testActionModeIconsOnImageView() {
+        // Tests that the app:scrCompat attribute set to ?attr/actionModeXYZDrawable gets
+        // resolved to non-null drawables
+        activityScenario.onActivity {
+            val imageViewCut = it.findViewById<AppCompatImageView>(R.id.icon_cut)
+            val imageViewCopy = it.findViewById<AppCompatImageView>(R.id.icon_copy)
+            val imageViewPaste = it.findViewById<AppCompatImageView>(R.id.icon_paste)
+            val imageViewSelectAll = it.findViewById<AppCompatImageView>(R.id.icon_selectall)
+            val imageViewShare = it.findViewById<AppCompatImageView>(R.id.icon_share)
+
+            assertNotNull(imageViewCut.drawable)
+            assertNotNull(imageViewCopy.drawable)
+            assertNotNull(imageViewPaste.drawable)
+            assertNotNull(imageViewSelectAll.drawable)
+            assertNotNull(imageViewShare.drawable)
+        }
+    }
+
+    @Test
+    fun testActionModeIconsFromAppCompatResources() {
+        // Tests that we can resolve R.attr.actionModeXYZDrawable to a non-null drawable
+        // from our activity's theme
+
+        activityScenario.onActivity {
+            // Resolve ?attr/actionModeCutDrawable
+            val typedValueCut = TypedValue()
+            it.theme.resolveAttribute(
+                androidx.appcompat.R.attr.actionModeCutDrawable, typedValueCut, true
+            )
+            val drawableCut = AppCompatResources.getDrawable(it, typedValueCut.resourceId)
+            assertNotNull(drawableCut)
+
+            // Resolve ?attr/actionModeCopyDrawable
+            val typedValueCopy = TypedValue()
+            it.theme.resolveAttribute(
+                androidx.appcompat.R.attr.actionModeCopyDrawable, typedValueCopy, true
+            )
+            val drawableCopy = AppCompatResources.getDrawable(it, typedValueCopy.resourceId)
+            assertNotNull(drawableCopy)
+
+            // Resolve ?attr/actionModePasteDrawable
+            val typedValuePaste = TypedValue()
+            it.theme.resolveAttribute(
+                androidx.appcompat.R.attr.actionModePasteDrawable, typedValuePaste, true
+            )
+            val drawablePaste = AppCompatResources.getDrawable(it, typedValuePaste.resourceId)
+            assertNotNull(drawablePaste)
+
+            // Resolve ?attr/actionModeSelectAllDrawable
+            val typedValueSelectAll = TypedValue()
+            it.theme.resolveAttribute(
+                androidx.appcompat.R.attr.actionModeSelectAllDrawable, typedValueSelectAll, true
+            )
+            val drawableSelectAll =
+                AppCompatResources.getDrawable(it, typedValueSelectAll.resourceId)
+            assertNotNull(drawableSelectAll)
+
+            // Resolve ?attr/actionModeShareDrawable
+            val typedValueShare = TypedValue()
+            it.theme.resolveAttribute(
+                androidx.appcompat.R.attr.actionModeShareDrawable, typedValueShare, true
+            )
+            val drawableShare = AppCompatResources.getDrawable(it, typedValueShare.resourceId)
+            assertNotNull(drawableShare)
+        }
+    }
+}
\ No newline at end of file
diff --git a/appcompat/appcompat/src/androidTest/res/layout/appcompat_icons_activity.xml b/appcompat/appcompat/src/androidTest/res/layout/appcompat_icons_activity.xml
new file mode 100644
index 0000000..e4a95ce
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/res/layout/appcompat_icons_activity.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <ImageView
+            android:id="@+id/icon_cut"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:srcCompat="?attr/actionModeCutDrawable" />
+
+        <ImageView
+            android:id="@+id/icon_copy"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:srcCompat="?attr/actionModeCopyDrawable" />
+
+        <ImageView
+            android:id="@+id/icon_paste"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:srcCompat="?attr/actionModePasteDrawable" />
+
+        <ImageView
+            android:id="@+id/icon_selectall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:srcCompat="?attr/actionModeSelectAllDrawable" />
+
+        <ImageView
+            android:id="@+id/icon_share"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:srcCompat="?attr/actionModeShareDrawable" />
+
+    </LinearLayout>
+
+</ScrollView>
diff --git a/appcompat/appcompat/src/androidTest/res/values/strings.xml b/appcompat/appcompat/src/androidTest/res/values/strings.xml
index 21d1659..19145b8 100644
--- a/appcompat/appcompat/src/androidTest/res/values/strings.xml
+++ b/appcompat/appcompat/src/androidTest/res/values/strings.xml
@@ -67,6 +67,7 @@
     <string name="app_compat_checkbox_activity">AppCompat checkbox</string>
     <string name="app_compat_radio_button_activity">AppCompat radio button</string>
     <string name="app_compat_toggle_button_activity">AppCompat toggle button</string>
+    <string name="app_compat_icons_activity">AppCompat icons</string>
     <string name="switch_compat_activity">Switch compat</string>
     <string-array name="planets_array">
         <item>Mercury</item>
diff --git a/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index 706fc1f..0000000
--- a/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_cut_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index e78bcaf..0000000
--- a/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_paste_mtrl_am_alpha.png
deleted file mode 100644
index 8610c50..0000000
--- a/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_selectall_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_selectall_mtrl_alpha.png
deleted file mode 100644
index 63d0e5d..0000000
--- a/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_share_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_share_mtrl_alpha.png
deleted file mode 100644
index cd1f57c..0000000
--- a/appcompat/appcompat/src/main/res/drawable-hdpi/abc_ic_menu_share_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index 559b835..0000000
--- a/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_cut_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index a282219..0000000
--- a/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_paste_mtrl_am_alpha.png
deleted file mode 100644
index 1492ab6..0000000
--- a/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_selectall_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_selectall_mtrl_alpha.png
deleted file mode 100644
index 7c011af..0000000
--- a/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_share_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_share_mtrl_alpha.png
deleted file mode 100644
index 36f664c..0000000
--- a/appcompat/appcompat/src/main/res/drawable-mdpi/abc_ic_menu_share_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index 8e664eb..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_cut_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index cd38901..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
deleted file mode 100644
index 9aabc43..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_selectall_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_selectall_mtrl_alpha.png
deleted file mode 100644
index c8bae19..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_share_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_share_mtrl_alpha.png
deleted file mode 100644
index 6be7e09..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xhdpi/abc_ic_menu_share_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index 90d6ba3..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index 63e541f..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
deleted file mode 100644
index f71485c..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_selectall_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
deleted file mode 100644
index 162ab9847..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_share_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_share_mtrl_alpha.png
deleted file mode 100644
index d95a3774..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xxhdpi/abc_ic_menu_share_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index 6758084..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index 397fd91..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
deleted file mode 100644
index 6c8428a..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_selectall_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
deleted file mode 100644
index 9084c38..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_share_mtrl_alpha.png b/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_share_mtrl_alpha.png
deleted file mode 100644
index ba16aac..0000000
--- a/appcompat/appcompat/src/main/res/drawable-xxxhdpi/abc_ic_menu_share_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_copy_mtrl_am_alpha.xml b/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_copy_mtrl_am_alpha.xml
new file mode 100644
index 0000000..60ebf76
--- /dev/null
+++ b/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_copy_mtrl_am_alpha.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector android:autoMirrored="true" android:height="24dp"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#ffffff" android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
+</vector>
diff --git a/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_cut_mtrl_alpha.xml b/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_cut_mtrl_alpha.xml
new file mode 100644
index 0000000..592bd60
--- /dev/null
+++ b/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_cut_mtrl_alpha.xml
@@ -0,0 +1,21 @@
+<!--
+  Copyright 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector android:autoMirrored="true" android:height="24dp"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#ffffff" android:pathData="M9.64,7.64c0.23,-0.5 0.36,-1.05 0.36,-1.64 0,-2.21 -1.79,-4 -4,-4S2,3.79 2,6s1.79,4 4,4c0.59,0 1.14,-0.13 1.64,-0.36L10,12l-2.36,2.36C7.14,14.13 6.59,14 6,14c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4c0,-0.59 -0.13,-1.14 -0.36,-1.64L12,14l7,7h3v-1L9.64,7.64zM6,8c-1.1,0 -2,-0.89 -2,-2s0.9,-2 2,-2 2,0.89 2,2 -0.9,2 -2,2zM6,20c-1.1,0 -2,-0.89 -2,-2s0.9,-2 2,-2 2,0.89 2,2 -0.9,2 -2,2zM12,12.5c-0.28,0 -0.5,-0.22 -0.5,-0.5s0.22,-0.5 0.5,-0.5 0.5,0.22 0.5,0.5 -0.22,0.5 -0.5,0.5zM19,3l-6,6 2,2 7,-7L22,3z"/>
+</vector>
diff --git a/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_paste_mtrl_am_alpha.xml b/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_paste_mtrl_am_alpha.xml
new file mode 100644
index 0000000..5404374
--- /dev/null
+++ b/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_paste_mtrl_am_alpha.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z"
+      android:fillColor="#ffffff"/>
+</vector>
diff --git a/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_selectall_mtrl_alpha.xml b/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_selectall_mtrl_alpha.xml
new file mode 100644
index 0000000..d0de4ae
--- /dev/null
+++ b/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_selectall_mtrl_alpha.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M3,5h2L5,3c-1.1,0 -2,0.9 -2,2zM3,13h2v-2L3,11v2zM7,21h2v-2L7,19v2zM3,9h2L5,7L3,7v2zM13,3h-2v2h2L13,3zM19,3v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2L3,19c0,1.1 0.9,2 2,2zM3,17h2v-2L3,15v2zM9,3L7,3v2h2L9,3zM11,21h2v-2h-2v2zM19,13h2v-2h-2v2zM19,21c1.1,0 2,-0.9 2,-2h-2v2zM19,9h2L21,7h-2v2zM19,17h2v-2h-2v2zM15,21h2v-2h-2v2zM15,5h2L17,3h-2v2zM7,17h10L17,7L7,7v10zM9,9h6v6L9,15L9,9z"
+      android:fillColor="#ffffff"/>
+</vector>
diff --git a/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_share_mtrl_alpha.xml b/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_share_mtrl_alpha.xml
new file mode 100644
index 0000000..597a1b3
--- /dev/null
+++ b/appcompat/appcompat/src/main/res/drawable/abc_ic_menu_share_mtrl_alpha.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright 2020 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"
+      android:fillColor="#ffffff"/>
+</vector>
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
index e055dda..c2f1a3e 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
@@ -16,6 +16,8 @@
 
 package androidx.build
 
+import androidx.build.dependencyTracker.AffectedModuleDetector
+import org.gradle.api.GradleException
 import org.gradle.api.Project
 
 /**
@@ -35,6 +37,60 @@
 const val ALL_WARNINGS_AS_ERRORS = "androidx.allWarningsAsErrors"
 
 /**
+ * Setting this property enables calculating the fraction of code covered by tests
+ */
+const val COVERAGE_ENABLED = "androidx.coverageEnabled"
+
+/**
+ * Setting this property puts a summary of the relevant failure messages into standard error
+ */
+const val SUMMARIZE_STANDARD_ERROR = "androidx.summarizeStderr"
+
+/**
+ * Setting this property enables writing versioned API files
+ */
+const val WRITE_VERSIONED_API_FILES = "androidx.writeVersionedApiFiles"
+
+/**
+ * Specifies the type of Android Studio to use for the project's Studio task
+ */
+const val STUDIO_TYPE = "androidx.studio.type"
+
+val ALL_ANDROIDX_PROPERTIES = setOf(
+    ALL_WARNINGS_AS_ERRORS,
+    COVERAGE_ENABLED,
+    DISPLAY_TEST_OUTPUT,
+    STUDIO_TYPE,
+    SUMMARIZE_STANDARD_ERROR,
+    TEST_FAILURES_DO_NOT_FAIL_TEST_TASK,
+    WRITE_VERSIONED_API_FILES,
+    AffectedModuleDetector.CHANGED_PROJECTS_ARG,
+    AffectedModuleDetector.ENABLE_ARG,
+    AffectedModuleDetector.DEPENDENT_PROJECTS_ARG,
+    AffectedModuleDetector.CHANGED_PROJECTS_ARG
+)
+
+/**
+ * Validates that all properties passed by the user of the form "-Pandroidx.*" are not misspelled
+ */
+fun Project.validateAllAndroidxArgumentsAreRecognized() {
+    for (propertyName in project.properties.keys) {
+        if (propertyName.startsWith("androidx")) {
+            if (!ALL_ANDROIDX_PROPERTIES.contains(propertyName)) {
+                val message = "Unrecognized Androidx property '$propertyName'.\n" +
+                    "\n" +
+                    "Is this a misspelling? All recognized Androidx properties:\n" +
+                    ALL_ANDROIDX_PROPERTIES.joinToString("\n") + "\n" +
+                    "\n" +
+                    "See AndroidXGradleProperties.kt if you need to add this property to " +
+                    "the list of known properties."
+                throw GradleException(message)
+            }
+        }
+    }
+}
+
+/**
  * Returns whether tests in the project should display output
  */
 fun Project.isDisplayTestOutput(): Boolean =
@@ -48,7 +104,7 @@
  * is `true`.
  */
 fun Project.isWriteVersionedApiFilesEnabled(): Boolean =
-    (project.findProperty("androidx.writeVersionedApiFiles") as? String)?.toBoolean() ?: true
+    (project.findProperty(WRITE_VERSIONED_API_FILES) as? String)?.toBoolean() ?: true
 
 /**
  * Returns whether the project should generate documentation.
@@ -60,13 +116,13 @@
  * Returns whether the project has coverage enabled.
  */
 fun Project.isCoverageEnabled(): Boolean =
-    (project.findProperty("androidx.coverageEnabled") as? String)?.toBoolean() ?: false
+    (project.findProperty(COVERAGE_ENABLED) as? String)?.toBoolean() ?: false
 
 /**
  * Returns the Studio type for the project's studio task
  */
 fun Project.studioType() = StudioType.findType(
-    findProperty("androidx.studio.type")?.toString()
+    findProperty(STUDIO_TYPE)?.toString()
 )
 
 enum class StudioType {
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt
index 1c47c0b..54598a6 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXRootPlugin.kt
@@ -59,6 +59,8 @@
         // TODO have the normal license check run here so it catches the buildscript classpath.
         tasks.register(CheckExternalDependencyLicensesTask.TASK_NAME)
 
+        project.validateAllAndroidxArgumentsAreRecognized()
+        tasks.register("listAndroidXProperties", ListAndroidXPropertiesTask::class.java)
         setDependencyVersions()
         configureKtlintCheckFile()
         configureCheckInvalidSuppress()
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index 1a28e14..6a4a957 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -59,7 +59,7 @@
     val DYNAMICANIMATION_KTX = Version("1.0.0-alpha04")
     val EMOJI = Version("1.2.0-alpha02")
     val ENTERPRISE = Version("1.1.0-alpha02")
-    val EXIFINTERFACE = Version("1.3.0-rc01")
+    val EXIFINTERFACE = Version("1.4.0-alpha01")
     val FRAGMENT = Version("1.3.0-alpha09")
     val FUTURES = Version("1.2.0-alpha01")
     val GRIDLAYOUT = Version("1.1.0-alpha01")
@@ -76,9 +76,9 @@
     val LIFECYCLE = Version("2.3.0-beta01")
     val LIFECYCLE_EXTENSIONS = Version("2.2.0")
     val LOADER = Version("1.2.0-alpha01")
-    val MEDIA = Version("1.2.0-rc01")
-    val MEDIA2 = Version("1.1.0-alpha02")
-    val MEDIAROUTER = Version("1.2.0-beta01")
+    val MEDIA = Version("1.3.0-alpha01")
+    val MEDIA2 = Version("1.1.0-beta01")
+    val MEDIAROUTER = Version("1.2.0-rc01")
     val NAVIGATION = Version("2.4.0-alpha01")
     val PAGING = Version("3.0.0-alpha07")
     val PALETTE = Version("1.1.0-alpha01")
@@ -125,5 +125,5 @@
     val WEBKIT = Version("1.4.0-alpha01")
     val WINDOW = Version("1.0.0-alpha02")
     val WINDOW_SIDECAR = Version("0.1.0-alpha01")
-    val WORK = Version("2.5.0-alpha01")
+    val WORK = Version("2.5.0-alpha02")
 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/ListAndroidXPropertiesTask.kt b/buildSrc/src/main/kotlin/androidx/build/ListAndroidXPropertiesTask.kt
new file mode 100644
index 0000000..10656f4
--- /dev/null
+++ b/buildSrc/src/main/kotlin/androidx/build/ListAndroidXPropertiesTask.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.build
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.tasks.TaskAction
+
+/**
+ * Lists recognized properties whose names start with "androidx"
+ */
+abstract class ListAndroidXPropertiesTask() : DefaultTask() {
+    init {
+        group = "Help"
+        description = "Lists AndroidX-specific properties (specifiable via -Pandroidx.*)"
+    }
+
+    @TaskAction
+    fun exec() {
+        project.logger.lifecycle(ALL_ANDROIDX_PROPERTIES.joinToString("\n"))
+        project.logger.lifecycle("See AndroidXGradleProperties.kt for more information")
+    }
+}
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
index fa533f7..dcb0366 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
@@ -86,9 +86,9 @@
     companion object {
         private const val ROOT_PROP_NAME = "affectedModuleDetector"
         private const val LOG_FILE_NAME = "affected_module_detector_log.txt"
-        private const val ENABLE_ARG = "androidx.enableAffectedModuleDetection"
-        private const val DEPENDENT_PROJECTS_ARG = "androidx.dependentProjects"
-        private const val CHANGED_PROJECTS_ARG = "androidx.changedProjects"
+        public const val ENABLE_ARG = "androidx.enableAffectedModuleDetection"
+        public const val DEPENDENT_PROJECTS_ARG = "androidx.dependentProjects"
+        public const val CHANGED_PROJECTS_ARG = "androidx.changedProjects"
         @JvmStatic
         fun configure(gradle: Gradle, rootProject: Project) {
             val enabled = rootProject.hasProperty(ENABLE_ARG)
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraControlDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraControlDeviceTest.java
index 355256b..fed9c13 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraControlDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraControlDeviceTest.java
@@ -24,13 +24,13 @@
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
 
-import androidx.camera.core.CameraInfoUnavailableException;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.CameraX;
 import androidx.camera.core.CameraXConfig;
 import androidx.camera.core.FocusMeteringAction;
 import androidx.camera.core.FocusMeteringResult;
 import androidx.camera.core.ImageAnalysis;
+import androidx.camera.core.ImageProxy;
 import androidx.camera.core.MeteringPoint;
 import androidx.camera.core.SurfaceOrientedMeteringPointFactory;
 import androidx.camera.core.UseCase;
@@ -86,13 +86,11 @@
     private CameraUseCaseAdapter mCamera;
     private UseCase mBoundUseCase;
     private MeteringPoint mMeteringPoint1;
-    private ImageAnalysis.Analyzer mAnalyzer = (image) -> {
-        image.close();
-    };
+    private final ImageAnalysis.Analyzer mAnalyzer = ImageProxy::close;
 
     @Before
     public void setUp()
-            throws CameraInfoUnavailableException, ExecutionException, InterruptedException {
+            throws ExecutionException, InterruptedException {
         Context context = ApplicationProvider.getApplicationContext();
         CameraXConfig cameraXConfig = Camera2Config.defaultConfig();
         CameraX.initialize(context, cameraXConfig).get();
@@ -106,7 +104,7 @@
         mMeteringPoint1 = factory.createPoint(0, 0);
 
         ImageAnalysis useCase = new ImageAnalysis.Builder().build();
-        mCamera = CameraUtil.getCameraAndAttachUseCase(context, mCameraSelector,
+        mCamera = CameraUtil.createCameraAndAttachUseCase(context, mCameraSelector,
                 mBoundUseCase = useCase);
         useCase.setAnalyzer(CameraXExecutors.ioExecutor(), mAnalyzer);
     }
@@ -138,6 +136,12 @@
 
     @After
     public void tearDown() throws ExecutionException, InterruptedException, TimeoutException {
+        mInstrumentation.runOnMainSync(() ->
+                //TODO: The removeUseCases() call might be removed after clarifying the
+                // abortCaptures() issue in b/162314023.
+                mCamera.removeUseCases(mCamera.getUseCases())
+        );
+
         CameraX.shutdown().get(10000, TimeUnit.MILLISECONDS);
     }
 
@@ -150,7 +154,7 @@
                 new FocusMeteringAction.Builder(mMeteringPoint1,
                         FocusMeteringAction.FLAG_AE).build();
         ListenableFuture<FocusMeteringResult> future =
-                mCamera.getCameraControlInternal().startFocusAndMetering(action);
+                mCamera.getCameraControl().startFocusAndMetering(action);
 
         assertFutureCompletes(future);
     }
@@ -163,7 +167,7 @@
                 new FocusMeteringAction.Builder(mMeteringPoint1,
                         FocusMeteringAction.FLAG_AWB).build();
         ListenableFuture<FocusMeteringResult> future =
-                mCamera.getCameraControlInternal().startFocusAndMetering(action);
+                mCamera.getCameraControl().startFocusAndMetering(action);
 
         assertFutureCompletes(future);
     }
@@ -176,7 +180,7 @@
                 new FocusMeteringAction.Builder(mMeteringPoint1,
                         FocusMeteringAction.FLAG_AE | FocusMeteringAction.FLAG_AWB).build();
         ListenableFuture<FocusMeteringResult> future =
-                mCamera.getCameraControlInternal().startFocusAndMetering(action);
+                mCamera.getCameraControl().startFocusAndMetering(action);
 
         assertFutureCompletes(future);
     }
@@ -203,7 +207,7 @@
                         .build();
 
         ListenableFuture<FocusMeteringResult> future =
-                mCamera.getCameraControlInternal().startFocusAndMetering(action);
+                mCamera.getCameraControl().startFocusAndMetering(action);
 
         assertFutureCompletes(future);
     }
@@ -212,8 +216,8 @@
     public void cancelFocusMetering_futureCompletes() {
         FocusMeteringAction action =
                 new FocusMeteringAction.Builder(mMeteringPoint1).build();
-        mCamera.getCameraControlInternal().startFocusAndMetering(action);
-        ListenableFuture<Void> result = mCamera.getCameraControlInternal().cancelFocusAndMetering();
+        mCamera.getCameraControl().startFocusAndMetering(action);
+        ListenableFuture<Void> result = mCamera.getCameraControl().cancelFocusAndMetering();
 
         assertFutureCompletes(result);
     }
@@ -233,7 +237,7 @@
             }
         });
 
-        ListenableFuture<Void> result = mCamera.getCameraControlInternal().enableTorch(true);
+        ListenableFuture<Void> result = mCamera.getCameraControl().enableTorch(true);
 
         assertFutureCompletes(result);
     }
@@ -251,7 +255,7 @@
             }
         });
 
-        ListenableFuture<Void> result = mCamera.getCameraControlInternal().setZoomRatio(1.0f);
+        ListenableFuture<Void> result = mCamera.getCameraControl().setZoomRatio(1.0f);
 
         assertFutureCompletes(result);
     }
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
index b95e958d..9c5c2f8 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
@@ -79,6 +79,7 @@
     private Semaphore mAnalysisResultsSemaphore;
     private CameraSelector mCameraSelector;
     private Context mContext;
+    private CameraUseCaseAdapter mCamera;
 
     @Rule
     public TestRule mCameraRule = CameraUtil.grantCameraPermissionAndPreTest();
@@ -112,6 +113,14 @@
 
     @After
     public void tearDown() throws ExecutionException, InterruptedException, TimeoutException {
+        if (mCamera != null) {
+            mInstrumentation.runOnMainSync(() ->
+                    //TODO: The removeUseCases() call might be removed after clarifying the
+                    // abortCaptures() issue in b/162314023.
+                    mCamera.removeUseCases(mCamera.getUseCases())
+            );
+        }
+
         CameraX.shutdown().get(10000, TimeUnit.MILLISECONDS);
 
         if (mHandlerThread != null) {
@@ -136,8 +145,8 @@
         ImageAnalysis useCase = new ImageAnalysis.Builder().setTargetResolution(
                 GUARANTEED_RESOLUTION).setTargetRotation(
                 isRotateNeeded ? Surface.ROTATION_90 : Surface.ROTATION_0).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_FRONT_CAMERA,
-                useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_FRONT_CAMERA, useCase);
         useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
         assertThat(mAnalysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue();
 
@@ -168,7 +177,8 @@
                 GUARANTEED_RESOLUTION).setTargetRotation(
                 isRotateNeeded ? Surface.ROTATION_90 : Surface.ROTATION_0).build();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
         useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
 
         assertThat(mAnalysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue();
@@ -198,7 +208,7 @@
             throws InterruptedException {
         ImageAnalysis useCase = new ImageAnalysis.Builder().setBackpressureStrategy(
                 backpressureStrategy).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
         useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
 
         mAnalysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS);
@@ -212,9 +222,8 @@
     public void analyzerDoesNotAnalyzeImages_whenCameraIsNotOpen() throws InterruptedException {
         ImageAnalysis useCase = new ImageAnalysis.Builder().build();
         // Bind but do not start lifecycle
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                mCameraSelector, useCase);
-        camera.detachUseCases();
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
+        mCamera.detachUseCases();
 
         useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
         // Keep the lifecycle in an inactive state.
@@ -244,7 +253,7 @@
     @Test
     public void defaultAspectRatioWillBeSet_whenTargetResolutionIsNotSet() {
         ImageAnalysis useCase = new ImageAnalysis.Builder().build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
         ImageOutputConfig config = (ImageOutputConfig) useCase.getUseCaseConfig();
         assertThat(config.getTargetAspectRatio()).isEqualTo(AspectRatio.RATIO_4_3);
     }
@@ -258,7 +267,8 @@
         assertThat(useCase.getUseCaseConfig().containsOption(
                 ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO)).isFalse();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         assertThat(useCase.getUseCaseConfig().containsOption(
                 ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO)).isFalse();
@@ -277,7 +287,7 @@
     public void targetResolutionIsUpdatedAfterTargetRotationIsUpdated() {
         ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().setTargetResolution(
                 GUARANTEED_RESOLUTION).setTargetRotation(Surface.ROTATION_0).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, imageAnalysis);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, imageAnalysis);
 
         // Updates target rotation from ROTATION_0 to ROTATION_90.
         imageAnalysis.setTargetRotation(Surface.ROTATION_90);
@@ -295,7 +305,7 @@
     public void analyzerSetMultipleTimesInKeepOnlyLatestMode() throws Exception {
         ImageAnalysis useCase = new ImageAnalysis.Builder().setBackpressureStrategy(
                 STRATEGY_KEEP_ONLY_LATEST).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
 
         useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
         mAnalysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS);
@@ -329,11 +339,10 @@
         final ImageAnalysis useCase = new ImageAnalysis.Builder().build();
         UseCaseConfig<?> initialConfig = useCase.getUseCaseConfig();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                mCameraSelector, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
 
         mInstrumentation.runOnMainSync(() -> {
-            camera.removeUseCases(Collections.singleton(useCase));
+            mCamera.removeUseCases(Collections.singleton(useCase));
         });
 
         UseCaseConfig<?> configAfterUnbinding = useCase.getUseCaseConfig();
@@ -344,8 +353,7 @@
     public void targetRotationIsRetained_whenUseCaseIsReused() {
         ImageAnalysis useCase = new ImageAnalysis.Builder().build();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                mCameraSelector, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
 
         // Generally, the device can't be rotated to Surface.ROTATION_180. Therefore,
         // use it to do the test.
@@ -353,13 +361,13 @@
 
         mInstrumentation.runOnMainSync(() -> {
             // Check the target rotation is kept when the use case is unbound.
-            camera.removeUseCases(Collections.singleton(useCase));
+            mCamera.removeUseCases(Collections.singleton(useCase));
             assertThat(useCase.getTargetRotation()).isEqualTo(Surface.ROTATION_180);
         });
 
         // Check the target rotation is kept when the use case is rebound to the
         // lifecycle.
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
         assertThat(useCase.getTargetRotation()).isEqualTo(Surface.ROTATION_180);
     }
 
@@ -367,19 +375,18 @@
     public void useCaseCanBeReusedInSameCamera() throws InterruptedException {
         ImageAnalysis useCase = new ImageAnalysis.Builder().build();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                mCameraSelector, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
         useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
 
         assertThat(mAnalysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue();
 
         mInstrumentation.runOnMainSync(() -> {
-            camera.removeUseCases(Collections.singleton(useCase));
+            mCamera.removeUseCases(Collections.singleton(useCase));
         });
 
         mAnalysisResultsSemaphore = new Semaphore(/*permits=*/ 0);
         // Rebind the use case to the same camera.
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
 
         assertThat(mAnalysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue();
     }
@@ -388,20 +395,20 @@
     public void useCaseCanBeReusedInDifferentCamera() throws InterruptedException {
         ImageAnalysis useCase = new ImageAnalysis.Builder().build();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
                 CameraSelector.DEFAULT_BACK_CAMERA, useCase);
         useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
 
         assertThat(mAnalysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue();
 
         mInstrumentation.runOnMainSync(() -> {
-            camera.removeUseCases(Collections.singleton(useCase));
+            mCamera.removeUseCases(Collections.singleton(useCase));
         });
 
         mAnalysisResultsSemaphore = new Semaphore(/*permits=*/ 0);
         // Rebind the use case to different camera.
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_FRONT_CAMERA,
-                useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_FRONT_CAMERA, useCase);
 
         assertThat(mAnalysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue();
     }
@@ -417,7 +424,7 @@
     public void returnCorrectTargetRotation_afterUseCaseIsAttached() {
         ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().setTargetRotation(
                 Surface.ROTATION_180).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, imageAnalysis);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, imageAnalysis);
         assertThat(imageAnalysis.getTargetRotation()).isEqualTo(Surface.ROTATION_180);
     }
 
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
index 46b0986..7fc0216 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageCaptureTest.java
@@ -114,13 +114,6 @@
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public final class ImageCaptureTest {
-    @Rule
-    public TestRule mCameraRule = CameraUtil.grantCameraPermissionAndPreTest();
-
-    @Rule
-    public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(
-            Manifest.permission.WRITE_EXTERNAL_STORAGE);
-
     private static final Size DEFAULT_RESOLUTION = new Size(640, 480);
     private static final Size GUARANTEED_RESOLUTION = new Size(640, 480);
     @CameraSelector.LensFacing
@@ -128,13 +121,17 @@
     private static final CameraSelector BACK_SELECTOR =
             new CameraSelector.Builder().requireLensFacing(BACK_LENS_FACING).build();
     private static final int FLASH_MODE_UNKNOWN = -1;
-
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
-
+    @Rule
+    public TestRule mCameraRule = CameraUtil.grantCameraPermissionAndPreTest();
+    @Rule
+    public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(
+            Manifest.permission.WRITE_EXTERNAL_STORAGE);
     private ImageCapture.Builder mDefaultBuilder;
     private Executor mMainExecutor;
     private ContentResolver mContentResolver;
     private Context mContext;
+    private CameraUseCaseAdapter mCamera;
 
     private ImageCaptureConfig createNonRotatedConfiguration() {
         // Create a configuration with target rotation that matches the sensor rotation.
@@ -163,6 +160,14 @@
 
     @After
     public void tearDown() throws ExecutionException, InterruptedException, TimeoutException {
+        if (mCamera != null) {
+            mInstrumentation.runOnMainSync(() ->
+                    //TODO: The removeUseCases() call might be removed after clarifying the
+                    // abortCaptures() issue in b/162314023.
+                    mCamera.removeUseCases(mCamera.getUseCases())
+            );
+        }
+
         CameraX.shutdown().get(10000, TimeUnit.MILLISECONDS);
     }
 
@@ -171,7 +176,7 @@
         ImageCapture useCase = new ImageCapture.Builder().setTargetResolution(
                 DEFAULT_RESOLUTION).setTargetRotation(Surface.ROTATION_0).build();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
 
         ResolvableFuture<ImageProperties> imageProperties = ResolvableFuture.create();
         OnImageCapturedCallback callback = createMockOnImageCapturedCallback(imageProperties);
@@ -215,8 +220,8 @@
                 GUARANTEED_RESOLUTION).setTargetRotation(
                 isRotateNeeded ? Surface.ROTATION_90 : Surface.ROTATION_0).build();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_FRONT_CAMERA,
-                useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_FRONT_CAMERA, useCase);
 
         ResolvableFuture<ImageProperties> imageProperties = ResolvableFuture.create();
         OnImageCapturedCallback callback = createMockOnImageCapturedCallback(imageProperties);
@@ -248,7 +253,9 @@
                 GUARANTEED_RESOLUTION).setTargetRotation(
                 isRotateNeeded ? Surface.ROTATION_90 : Surface.ROTATION_0).build();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA,
+                useCase);
 
         ResolvableFuture<ImageProperties> imageProperties = ResolvableFuture.create();
         OnImageCapturedCallback callback = createMockOnImageCapturedCallback(imageProperties);
@@ -265,7 +272,8 @@
     @Test
     public void canCaptureMultipleImages() throws InterruptedException {
         ImageCapture useCase = mDefaultBuilder.build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         int numImages = 5;
         CountingCallback callback = new CountingCallback(numImages, 50000);
@@ -281,7 +289,8 @@
         ImageCapture useCase = new ImageCapture.Builder()
                 .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
                 .build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         int numImages = 5;
         CountingCallback callback = new CountingCallback(numImages, 50000);
@@ -295,7 +304,8 @@
     @Test
     public void saveCanSucceed() throws IOException {
         ImageCapture useCase = mDefaultBuilder.build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         File saveLocation = File.createTempFile("test", ".jpg");
         saveLocation.deleteOnExit();
@@ -311,7 +321,8 @@
     public void saveToUri() {
         // Arrange.
         ImageCapture useCase = mDefaultBuilder.build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         ContentValues contentValues = new ContentValues();
         contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
@@ -341,7 +352,8 @@
     public void saveToOutputStream() throws IOException {
         // Arrange.
         ImageCapture useCase = mDefaultBuilder.build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         File saveLocation = File.createTempFile("test", ".jpg");
         saveLocation.deleteOnExit();
@@ -363,7 +375,8 @@
                 android.os.Build.MODEL.contains("Cuttlefish"));
         ImageCapture useCase = new ImageCapture.Builder().setTargetRotation(
                 Surface.ROTATION_0).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         File saveLocation = File.createTempFile("test", ".jpg");
         saveLocation.deleteOnExit();
@@ -420,7 +433,8 @@
         // can be equivalent to flipping horizontally
         ImageCapture useCase = ImageCapture.Builder.fromConfig(
                 createNonRotatedConfiguration()).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         File saveLocation = File.createTempFile("test", ".jpg");
         saveLocation.deleteOnExit();
@@ -447,7 +461,8 @@
         // horizontally can be equivalent to flipping vertically
         ImageCapture useCase = ImageCapture.Builder.fromConfig(
                 createNonRotatedConfiguration()).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         File saveLocation = File.createTempFile("test", ".jpg");
         saveLocation.deleteOnExit();
@@ -470,7 +485,8 @@
     @Test
     public void canSaveFile_withAttachedLocation() throws IOException {
         ImageCapture useCase = mDefaultBuilder.build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         File saveLocation = File.createTempFile("test", ".jpg");
         saveLocation.deleteOnExit();
@@ -494,7 +510,8 @@
     @Test
     public void canSaveMultipleFiles() throws IOException {
         ImageCapture useCase = mDefaultBuilder.build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         OnImageSavedCallback callback = mock(OnImageSavedCallback.class);
         int numImages = 5;
@@ -514,7 +531,8 @@
     @Test
     public void saveWillFail_whenInvalidFilePathIsUsed() {
         ImageCapture useCase = mDefaultBuilder.build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         // Note the invalid path
         File saveLocation = new File("/not/a/real/path.jpg");
@@ -540,7 +558,8 @@
                 mock(CameraCaptureSession.CaptureCallback.class);
         new Camera2Interop.Extender<>(builder).setSessionCaptureCallback(captureCallback);
         ImageCapture useCase = builder.build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         OnImageCapturedCallback callback = createMockOnImageCapturedCallback(null);
         useCase.takePicture(mMainExecutor, callback);
@@ -578,7 +597,8 @@
         ImageCapture useCase = new ImageCapture.Builder()
                 .setBufferFormat(ImageFormat.RAW10)
                 .build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         ResolvableFuture<ImageProperties> imageProperties = ResolvableFuture.create();
         OnImageCapturedCallback callback = createMockOnImageCapturedCallback(imageProperties);
@@ -617,8 +637,8 @@
         ImageCapture imageCapture = new ImageCapture.Builder().setCaptureBundle(
                 captureBundle).build();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA,
-                imageCapture);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, imageCapture);
 
         OnImageCapturedCallback callback = createMockOnImageCapturedCallback(null);
         imageCapture.takePicture(mMainExecutor, callback);
@@ -650,8 +670,8 @@
                 .setCaptureProcessor(mock(CaptureProcessor.class))
                 .build();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA,
-                imageCapture);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, imageCapture);
 
         // Add an additional capture stage to test the case
         // captureStage.size() > mMaxCaptureStages during takePicture.
@@ -675,8 +695,8 @@
     @Test
     public void onStateOffline_abortAllCaptureRequests() throws InterruptedException {
         ImageCapture imageCapture = new ImageCapture.Builder().build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA,
-                imageCapture);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, imageCapture);
 
         // After the use case can be reused, the capture requests can only be cancelled after the
         // onStateAttached() callback has been received. In the normal code flow, the
@@ -708,9 +728,8 @@
     @Test
     public void unbind_abortAllCaptureRequests() throws InterruptedException {
         ImageCapture imageCapture = new ImageCapture.Builder().build();
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                CameraSelector.DEFAULT_BACK_CAMERA,
-                imageCapture);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, imageCapture);
 
         CountingCallback callback = new CountingCallback(3, 10000);
 
@@ -723,7 +742,7 @@
         // after ImageCapture is removed so errors out with a different error from
         // ERROR_CAMERA_CLOSED
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
-                camera.removeUseCases(Collections.singleton(imageCapture))
+                mCamera.removeUseCases(Collections.singleton(imageCapture))
         );
 
         assertThat(callback.getNumOnCaptureSuccess() + callback.getNumOnError()).isEqualTo(3);
@@ -758,7 +777,7 @@
     @Test
     public void defaultAspectRatioWillBeSet_whenTargetResolutionIsNotSet() {
         ImageCapture useCase = new ImageCapture.Builder().build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
         ImageOutputConfig config = (ImageOutputConfig) useCase.getUseCaseConfig();
         assertThat(config.getTargetAspectRatio()).isEqualTo(AspectRatio.RATIO_4_3);
     }
@@ -772,7 +791,7 @@
         assertThat(useCase.getUseCaseConfig().containsOption(
                 ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO)).isFalse();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
 
         assertThat(useCase.getUseCaseConfig().containsOption(
                 ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO)).isFalse();
@@ -791,7 +810,7 @@
     public void targetResolutionIsUpdatedAfterTargetRotationIsUpdated() {
         ImageCapture imageCapture = new ImageCapture.Builder().setTargetResolution(
                 DEFAULT_RESOLUTION).setTargetRotation(Surface.ROTATION_0).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR, imageCapture);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, imageCapture);
 
         // Updates target rotation from ROTATION_0 to ROTATION_90.
         imageCapture.setTargetRotation(Surface.ROTATION_90);
@@ -810,7 +829,7 @@
         ImageCapture useCase = new ImageCapture.Builder().setTargetResolution(
                 DEFAULT_RESOLUTION).build();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
 
         ResolvableFuture<ImageProperties> imagePropertiesFuture = ResolvableFuture.create();
         OnImageCapturedCallback callback = createMockOnImageCapturedCallback(imagePropertiesFuture);
@@ -852,7 +871,7 @@
                 DEFAULT_RESOLUTION).setTargetRotation(
                 isRotateNeeded ? Surface.ROTATION_90 : Surface.ROTATION_0).build();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
 
         ResolvableFuture<ImageProperties> imagePropertiesFuture = ResolvableFuture.create();
         OnImageCapturedCallback callback = createMockOnImageCapturedCallback(imagePropertiesFuture);
@@ -898,7 +917,7 @@
         // Updates target rotation to opposite one.
         useCase.setTargetRotation(isRotateNeeded ? Surface.ROTATION_0 : Surface.ROTATION_90);
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
 
         ResolvableFuture<ImageProperties> imagePropertiesFuture = ResolvableFuture.create();
         OnImageCapturedCallback callback = createMockOnImageCapturedCallback(imagePropertiesFuture);
@@ -937,7 +956,7 @@
             throws ExecutionException, InterruptedException {
         ImageCapture useCase = new ImageCapture.Builder().build();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
 
         ResolvableFuture<ImageProperties> imagePropertiesFuture = ResolvableFuture.create();
         OnImageCapturedCallback callback = createMockOnImageCapturedCallback(imagePropertiesFuture);
@@ -986,11 +1005,10 @@
         final ImageCapture useCase = mDefaultBuilder.build();
         UseCaseConfig<?> initialConfig = useCase.getUseCaseConfig();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR,
-                useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
 
         mInstrumentation.runOnMainSync(() -> {
-            camera.removeUseCases(Collections.singleton(useCase));
+            mCamera.removeUseCases(Collections.singleton(useCase));
         });
 
         UseCaseConfig<?> configAfterUnbinding = useCase.getUseCaseConfig();
@@ -1001,8 +1019,7 @@
     public void targetRotationIsRetained_whenUseCaseIsReused() {
         ImageCapture useCase = mDefaultBuilder.build();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR,
-                useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
 
         // Generally, the device can't be rotated to Surface.ROTATION_180. Therefore,
         // use it to do the test.
@@ -1010,7 +1027,7 @@
 
         mInstrumentation.runOnMainSync(() -> {
             // Unbind the use case.
-            camera.removeUseCases(Collections.singleton(useCase));
+            mCamera.removeUseCases(Collections.singleton(useCase));
         });
 
         // Check the target rotation is kept when the use case is unbound.
@@ -1018,7 +1035,7 @@
 
         // Check the target rotation is kept when the use case is rebound to the
         // lifecycle.
-        CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
         assertThat(useCase.getTargetRotation()).isEqualTo(Surface.ROTATION_180);
     }
 
@@ -1028,17 +1045,16 @@
         ImageCapture useCase = mDefaultBuilder.build();
         Rational cropAspectRatio = new Rational(1, 1);
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                BACK_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
         useCase.setCropAspectRatio(cropAspectRatio);
 
         mInstrumentation.runOnMainSync(() -> {
             // Unbind the use case.
-            camera.removeUseCases(Collections.singleton(useCase));
+            mCamera.removeUseCases(Collections.singleton(useCase));
         });
 
         // Rebind the use case.
-        CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
 
         ResolvableFuture<ImageProperties> imagePropertiesFuture = ResolvableFuture.create();
         OnImageCapturedCallback callback = createMockOnImageCapturedCallback(imagePropertiesFuture);
@@ -1059,8 +1075,7 @@
     public void useCaseCanBeReusedInSameCamera() throws IOException {
         ImageCapture useCase = mDefaultBuilder.build();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                BACK_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
 
         File saveLocation1 = File.createTempFile("test1", ".jpg");
         saveLocation1.deleteOnExit();
@@ -1072,11 +1087,11 @@
 
         mInstrumentation.runOnMainSync(() -> {
             // Unbind the use case.
-            camera.removeUseCases(Collections.singleton(useCase));
+            mCamera.removeUseCases(Collections.singleton(useCase));
         });
 
         // Rebind the use case to the same camera.
-        CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, useCase);
 
         File saveLocation2 = File.createTempFile("test2", ".jpg");
         saveLocation2.deleteOnExit();
@@ -1091,7 +1106,7 @@
     public void useCaseCanBeReusedInDifferentCamera() throws IOException {
         ImageCapture useCase = mDefaultBuilder.build();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
                 CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         File saveLocation1 = File.createTempFile("test1", ".jpg");
@@ -1104,12 +1119,12 @@
 
         mInstrumentation.runOnMainSync(() -> {
             // Unbind the use case.
-            camera.removeUseCases(Collections.singleton(useCase));
+            mCamera.removeUseCases(Collections.singleton(useCase));
         });
 
         // Rebind the use case to different camera.
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_FRONT_CAMERA,
-                useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_FRONT_CAMERA, useCase);
 
         File saveLocation2 = File.createTempFile("test2", ".jpg");
         saveLocation2.deleteOnExit();
@@ -1131,7 +1146,7 @@
     public void returnCorrectTargetRotation_afterUseCaseIsAttached() {
         ImageCapture imageCapture = new ImageCapture.Builder().setTargetRotation(
                 Surface.ROTATION_180).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR, imageCapture);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, imageCapture);
         assertThat(imageCapture.getTargetRotation()).isEqualTo(Surface.ROTATION_180);
     }
 
@@ -1145,19 +1160,10 @@
     public void returnCorrectFlashMode_afterUseCaseIsAttached() {
         ImageCapture imageCapture = new ImageCapture.Builder().setFlashMode(
                 ImageCapture.FLASH_MODE_ON).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, BACK_SELECTOR, imageCapture);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, BACK_SELECTOR, imageCapture);
         assertThat(imageCapture.getFlashMode()).isEqualTo(ImageCapture.FLASH_MODE_ON);
     }
 
-    private static final class ImageProperties {
-        public Size size;
-        public int format;
-        public int rotationDegrees;
-        public Rect cropRect;
-
-        public Exif exif;
-    }
-
     private OnImageCapturedCallback createMockOnImageCapturedCallback(
             @Nullable ResolvableFuture<ImageProperties> resultProperties) {
         OnImageCapturedCallback callback = mock(OnImageCapturedCallback.class);
@@ -1191,14 +1197,22 @@
         return callback;
     }
 
+    private static final class ImageProperties {
+        public Size size;
+        public int format;
+        public int rotationDegrees;
+        public Rect cropRect;
+
+        public Exif exif;
+    }
+
     private static class CountingCallback extends OnImageCapturedCallback {
         CountDownLatch mCountDownLatch;
         long mTimeout;
+        List<Integer> mImageCaptureErrors = new ArrayList<>();
         private int mNumOnCaptureSuccess = 0;
         private int mNumOnErrorSuccess = 0;
 
-        List<Integer> mImageCaptureErrors = new ArrayList<>();
-
         CountingCallback(int numTakePictures, long timeout) {
             mTimeout = timeout;
             mCountDownLatch = new CountDownLatch(numTakePictures);
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
index ad2fc3d..e0c776a 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
@@ -90,6 +90,7 @@
     private Semaphore mSurfaceFutureSemaphore;
     private Semaphore mSafeToReleaseSemaphore;
     private Context mContext;
+    private CameraUseCaseAdapter mCamera;
 
     @Before
     public void setUp() throws ExecutionException, InterruptedException {
@@ -105,6 +106,14 @@
 
     @After
     public void tearDown() throws ExecutionException, InterruptedException, TimeoutException {
+        if (mCamera != null) {
+            mInstrumentation.runOnMainSync(() ->
+                    //TODO: The removeUseCases() call might be removed after clarifying the
+                    // abortCaptures() issue in b/162314023.
+                    mCamera.removeUseCases(mCamera.getUseCases())
+            );
+        }
+
         // Ensure all cameras are released for the next test
         CameraX.shutdown().get(10000, TimeUnit.MILLISECONDS);
     }
@@ -122,7 +131,7 @@
         //  done on the main thread
         mInstrumentation.runOnMainSync(() -> preview.setSurfaceProvider(surfaceProvider));
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, preview);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, preview);
 
         verify(surfaceProvider, timeout(3000)).onSurfaceRequested(any(SurfaceRequest.class));
     }
@@ -139,14 +148,13 @@
                 preview.setSurfaceProvider(CameraXExecutors.mainThreadExecutor(),
                 getSurfaceProvider(null))
         );
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                mCameraSelector, preview);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, preview);
 
         // Wait until preview gets frame.
         assertThat(mSurfaceFutureSemaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue();
 
         // Remove the UseCase from the camera
-        camera.removeUseCases(Collections.singleton(preview));
+        mCamera.removeUseCases(Collections.singleton(preview));
 
         // Assert.
         assertThat(mSafeToReleaseSemaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue();
@@ -161,7 +169,7 @@
         mInstrumentation.runOnMainSync(() -> preview.setSurfaceProvider(getSurfaceProvider(null)));
 
         // Act.
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, preview);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, preview);
 
         // Assert.
         assertThat(mSurfaceFutureSemaphore.tryAcquire(10, TimeUnit.SECONDS)).isTrue();
@@ -182,7 +190,7 @@
         );
 
         // Act.
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, preview);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, preview);
 
         // Assert.
         assertThat(mSurfaceFutureSemaphore.tryAcquire(10, TimeUnit.SECONDS)).isTrue();
@@ -193,7 +201,7 @@
     public void setSurfaceProviderAfterAttach_getsFrame() throws InterruptedException {
         // Arrange.
         Preview preview = mDefaultBuilder.build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, preview);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, preview);
 
         // Act.
         // TODO(b/160261462) move off of main thread when setSurfaceProvider does not need to be
@@ -211,7 +219,7 @@
 
         // Arrange.
         Preview preview = mDefaultBuilder.build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, preview);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, preview);
 
         // Act.
         // TODO(b/160261462) move off of main thread when setSurfaceProvider does not need to be
@@ -247,7 +255,7 @@
         // TODO(b/160261462) move off of main thread when setSurfaceProvider does not need to be
         //  done on the main thread
         mInstrumentation.runOnMainSync(() -> preview.setSurfaceProvider(getSurfaceProvider(null)));
-        CameraUtil.getCameraAndAttachUseCase(mContext,
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
                 CameraSelector.DEFAULT_FRONT_CAMERA, preview);
 
         // Assert.
@@ -280,7 +288,7 @@
         // TODO(b/160261462) move off of main thread when setSurfaceProvider does not need to be
         //  done on the main thread
         mInstrumentation.runOnMainSync(() -> preview.setSurfaceProvider(getSurfaceProvider(null)));
-        CameraUtil.getCameraAndAttachUseCase(mContext,
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
                 CameraSelector.DEFAULT_BACK_CAMERA, preview);
 
         // Assert.
@@ -303,7 +311,7 @@
             // for preview.
             preview.setSurfaceProvider(getSurfaceProvider(null));
         });
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, preview);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, preview);
 
         assertThat(mSurfaceFutureSemaphore.tryAcquire(10, TimeUnit.SECONDS)).isTrue();
 
@@ -329,7 +337,7 @@
             // for preview.
             preview.setSurfaceProvider(getSurfaceProvider(null));
         });
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, preview);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, preview);
 
         assertThat(mSurfaceFutureSemaphore.tryAcquire(10, TimeUnit.SECONDS)).isTrue();
 
@@ -348,7 +356,7 @@
     @Test
     public void defaultAspectRatioWillBeSet_whenTargetResolutionIsNotSet() {
         Preview useCase = new Preview.Builder().build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
         ImageOutputConfig config = (ImageOutputConfig) useCase.getUseCaseConfig();
         assertThat(config.getTargetAspectRatio()).isEqualTo(AspectRatio.RATIO_4_3);
     }
@@ -361,7 +369,8 @@
         assertThat(useCase.getUseCaseConfig().containsOption(
                 ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO)).isFalse();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_BACK_CAMERA, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_BACK_CAMERA, useCase);
 
         assertThat(useCase.getUseCaseConfig().containsOption(
                 ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO)).isFalse();
@@ -372,11 +381,10 @@
         final Preview preview = mDefaultBuilder.build();
         UseCaseConfig<?> initialConfig = preview.getUseCaseConfig();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                mCameraSelector, preview);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, preview);
 
         mInstrumentation.runOnMainSync(() -> {
-            camera.removeUseCases(Collections.singleton(preview));
+            mCamera.removeUseCases(Collections.singleton(preview));
         });
 
         UseCaseConfig<?> configAfterUnbinding = preview.getUseCaseConfig();
@@ -387,8 +395,7 @@
     public void targetRotationIsRetained_whenUseCaseIsReused() {
         Preview useCase = mDefaultBuilder.build();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                mCameraSelector, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
 
         // Generally, the device can't be rotated to Surface.ROTATION_180. Therefore,
         // use it to do the test.
@@ -396,7 +403,7 @@
 
         mInstrumentation.runOnMainSync(() -> {
             // Unbind the use case.
-            camera.removeUseCases(Collections.singleton(useCase));
+            mCamera.removeUseCases(Collections.singleton(useCase));
         });
 
         // Check the target rotation is kept when the use case is unbound.
@@ -404,7 +411,7 @@
 
         // Check the target rotation is kept when the use case is rebound to the
         // lifecycle.
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, useCase);
         assertThat(useCase.getTargetRotation()).isEqualTo(Surface.ROTATION_180);
     }
 
@@ -417,15 +424,14 @@
         });
 
         // This is the first time the use case bound to the lifecycle.
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                mCameraSelector, preview);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, preview);
 
         // Check the frame available callback is called.
         assertThat(mSurfaceFutureSemaphore.tryAcquire(10, TimeUnit.SECONDS)).isTrue();
 
         mInstrumentation.runOnMainSync(() -> {
             // Unbind and rebind the use case to the same lifecycle.
-            camera.removeUseCases(Collections.singleton(preview));
+            mCamera.removeUseCases(Collections.singleton(preview));
         });
 
         assertThat(mSafeToReleaseSemaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue();
@@ -433,7 +439,7 @@
         // Recreate the semaphore to monitor the frame available callback.
         mSurfaceFutureSemaphore = new Semaphore(/*permits=*/ 0);
         // Rebind the use case to the same camera.
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, preview);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, preview);
 
         // Check the frame available callback can be called after reusing the use case.
         assertThat(mSurfaceFutureSemaphore.tryAcquire(10, TimeUnit.SECONDS)).isTrue();
@@ -448,7 +454,7 @@
         });
 
         // This is the first time the use case bound to the lifecycle.
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
                 CameraSelector.DEFAULT_BACK_CAMERA, preview);
 
         // Check the frame available callback is called.
@@ -456,7 +462,7 @@
 
         mInstrumentation.runOnMainSync(() -> {
             // Unbind and rebind the use case to the same lifecycle.
-            camera.removeUseCases(Collections.singleton(preview));
+            mCamera.removeUseCases(Collections.singleton(preview));
         });
 
         assertThat(mSafeToReleaseSemaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue();
@@ -464,8 +470,8 @@
         // Recreate the semaphore to monitor the frame available callback.
         mSurfaceFutureSemaphore = new Semaphore(/*permits=*/ 0);
         // Rebind the use case to different camera.
-        CameraUtil.getCameraAndAttachUseCase(mContext, CameraSelector.DEFAULT_FRONT_CAMERA,
-                preview);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                CameraSelector.DEFAULT_FRONT_CAMERA, preview);
 
         // Check the frame available callback can be called after reusing the use case.
         assertThat(mSurfaceFutureSemaphore.tryAcquire(10, TimeUnit.SECONDS)).isTrue();
@@ -482,7 +488,7 @@
     public void returnCorrectTargetRotation_afterUseCaseIsAttached() {
         Preview preview = new Preview.Builder().setTargetRotation(
                 Surface.ROTATION_180).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, preview);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, preview);
         assertThat(preview.getTargetRotation()).isEqualTo(Surface.ROTATION_180);
     }
 
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SurfaceOrientedMeteringPointFactoryTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SurfaceOrientedMeteringPointFactoryTest.java
index b87901b..d7a27a3 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SurfaceOrientedMeteringPointFactoryTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SurfaceOrientedMeteringPointFactoryTest.java
@@ -31,8 +31,10 @@
 import androidx.camera.core.MeteringPoint;
 import androidx.camera.core.MeteringPointFactory;
 import androidx.camera.core.SurfaceOrientedMeteringPointFactory;
+import androidx.camera.core.internal.CameraUseCaseAdapter;
 import androidx.camera.testing.CameraUtil;
 import androidx.test.core.app.ApplicationProvider;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.After;
 import org.junit.Before;
@@ -116,12 +118,19 @@
         CameraSelector cameraSelector =
                 new CameraSelector.Builder().requireLensFacing(
                         CameraSelector.LENS_FACING_BACK).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, cameraSelector, imageAnalysis);
+        CameraUseCaseAdapter camera = CameraUtil.createCameraAndAttachUseCase(mContext,
+                cameraSelector, imageAnalysis);
 
         SurfaceOrientedMeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(
                 WIDTH, HEIGHT, imageAnalysis);
         MeteringPoint point = factory.createPoint(0f, 0f);
         assertThat(point.getSurfaceAspectRatio()).isEqualTo(new Rational(4, 3));
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
+                //TODO: The removeUseCases() call might be removed after clarifying the
+                // abortCaptures() issue in b/162314023.
+                camera.removeUseCases(camera.getUseCases())
+        );
     }
 
     @Test(expected = IllegalStateException.class)
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
index 757b47f9..1406fb1 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
@@ -77,7 +77,7 @@
         final Preview preview = initPreview();
         final ImageCapture imageCapture = initImageCapture();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraUseCaseAdapter(mContext,
+        CameraUseCaseAdapter camera = CameraUtil.createCameraUseCaseAdapter(mContext,
                 DEFAULT_SELECTOR);
         camera.detachUseCases();
 
@@ -101,7 +101,7 @@
         final Preview preview = initPreview();
         final ImageAnalysis imageAnalysis = initImageAnalysis();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraUseCaseAdapter(mContext,
+        CameraUseCaseAdapter camera = CameraUtil.createCameraUseCaseAdapter(mContext,
                 DEFAULT_SELECTOR);
         camera.detachUseCases();
 
@@ -124,7 +124,7 @@
         final ImageAnalysis imageAnalysis = initImageAnalysis();
         final ImageCapture imageCapture = initImageCapture();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraUseCaseAdapter(mContext,
+        CameraUseCaseAdapter camera = CameraUtil.createCameraUseCaseAdapter(mContext,
                 DEFAULT_SELECTOR);
         camera.detachUseCases();
 
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplTest.java
index 577b6e0..2dd1819 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplTest.java
@@ -53,7 +53,6 @@
 import androidx.annotation.NonNull;
 import androidx.camera.camera2.Camera2Config;
 import androidx.camera.camera2.impl.Camera2ImplConfig;
-import androidx.camera.core.Camera;
 import androidx.camera.core.CameraControl;
 import androidx.camera.core.CameraSelector;
 import androidx.camera.core.CameraX;
@@ -117,7 +116,7 @@
     private CameraCharacteristics mCameraCharacteristics;
     private boolean mHasFlashUnit;
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
-    private Camera mCamera;
+    private CameraUseCaseAdapter mCamera;
 
     @Before
     public void setUp() throws InterruptedException {
@@ -153,6 +152,14 @@
 
     @After
     public void tearDown() throws InterruptedException, ExecutionException, TimeoutException {
+        if (mCamera != null) {
+            mInstrumentation.runOnMainSync(() ->
+                    //TODO: The removeUseCases() call might be removed after clarifying the
+                    // abortCaptures() issue in b/162314023.
+                    mCamera.removeUseCases(mCamera.getUseCases())
+            );
+        }
+
         CameraX.shutdown().get(10000, TimeUnit.MILLISECONDS);
         if (mHandlerThread != null) {
             mHandlerThread.quitSafely();
@@ -365,17 +372,16 @@
         future.get(5, TimeUnit.SECONDS);
     }
 
-
     private Camera2CameraControlImpl createCamera2CameraControlWithPhysicalCamera() {
         ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().build();
         // Make ImageAnalysis active.
         imageAnalysis.setAnalyzer(CameraXExecutors.mainThreadExecutor(), (image) -> image.close());
 
-        CameraUseCaseAdapter cameraUseCaseAdapter =
-                CameraUtil.getCameraAndAttachUseCase(ApplicationProvider.getApplicationContext(),
-                        CameraSelector.DEFAULT_BACK_CAMERA, imageAnalysis);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(
+                ApplicationProvider.getApplicationContext(), CameraSelector.DEFAULT_BACK_CAMERA,
+                imageAnalysis);
 
-        return (Camera2CameraControlImpl) cameraUseCaseAdapter.getCameraControlInternal();
+        return (Camera2CameraControlImpl) mCamera.getCameraControl();
     }
 
     @Test
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java
index ba69473..0cf4896 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2ImplCameraXTest.java
@@ -95,6 +95,7 @@
     private FakeLifecycleOwner mLifecycle;
 
     private Context mContext;
+    private CameraUseCaseAdapter mCamera;
 
     @Before
     public void setUp() {
@@ -107,6 +108,14 @@
 
     @After
     public void tearDown() throws InterruptedException, ExecutionException, TimeoutException {
+        if (mCamera != null) {
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
+                    //TODO: The removeUseCases() call might be removed after clarifying the
+                    // abortCaptures() issue in b/162314023.
+                    mCamera.removeUseCases(mCamera.getUseCases())
+            );
+        }
+
         CameraX.shutdown().get(10000, TimeUnit.MILLISECONDS);
     }
 
@@ -116,7 +125,7 @@
         new Camera2Interop.Extender<>(builder).setDeviceStateCallback(mDeviceStateCallback);
         ImageAnalysis useCase = builder.build();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase);
         WaitingAnalyzer waitingAnalyzer = new WaitingAnalyzer(10);
         useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), waitingAnalyzer);
 
@@ -142,8 +151,7 @@
 
         ImageAnalysis useCase2 = new ImageAnalysis.Builder().build();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                DEFAULT_SELECTOR, useCase,
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase,
                 useCase2);
 
         CountingAnalyzer countingAnalyzer = new CountingAnalyzer();
@@ -155,7 +163,7 @@
         // TODO(b/160249108) move off of main thread once UseCases can be attached on any
         //  thread
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
-                camera.removeUseCases(Arrays.asList(useCase))
+                mCamera.removeUseCases(Arrays.asList(useCase))
         );
 
         mLifecycle.startAndResume();
@@ -176,9 +184,7 @@
                 .setDeviceStateCallback(deviceStateCallback)
                 .setSessionCaptureCallback(sessionCaptureCallback);
         ImageAnalysis useCase = configBuilder.build();
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                DEFAULT_SELECTOR,
-                useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase);
         CountingAnalyzer countingAnalyzer = new CountingAnalyzer();
         useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), countingAnalyzer);
 
@@ -187,7 +193,7 @@
         // Wait a little bit for the camera to open and stream frames.
         sessionCaptureCallback.waitForOnCaptureCompleted(5);
 
-        camera.detachUseCases();
+        mCamera.detachUseCases();
 
         // Wait a little bit for the camera to close.
         deviceStateCallback.waitForOnClosed(1);
@@ -241,8 +247,7 @@
         ImageAnalysis useCase0 = configBuilder0.build();
         CameraSelector selectorBack = new CameraSelector.Builder().requireLensFacing(
                 CameraSelector.LENS_FACING_BACK).build();
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext, selectorBack,
-                useCase0);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, selectorBack, useCase0);
 
         mLifecycle.startAndResume();
 
@@ -255,14 +260,14 @@
             // camera capture session will be opened immediately after a previous
             // CaptureSession is opened, and the previous CaptureSession will going to close
             // right after the new CaptureSession is opened.
-            camera.detachUseCases();
-            camera.attachUseCases();
+            mCamera.detachUseCases();
+            mCamera.attachUseCases();
             // Wait for the capture session is configured.
             assertTrue(sessionStateCallback.waitForOnConfigured(1));
         }
 
         // Detach all useCase and switch to another camera to verify the camera close flow.
-        camera.detachUseCases();
+        mCamera.detachUseCases();
 
         // The camera switch only succeeds after all the exist CaptureSession was
         // closed successfully.
@@ -272,7 +277,7 @@
         ImageAnalysis useCase1 = configBuilder1.build();
         CameraSelector selectorFront = new CameraSelector.Builder().requireLensFacing(
                 CameraSelector.LENS_FACING_FRONT).build();
-        CameraUtil.getCameraAndAttachUseCase(mContext, selectorFront, useCase1);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, selectorFront, useCase1);
 
         // The front camera should open successfully. If the test fail, the CameraX might
         // in wrong internal state, and the CameraX#shutdown() might stuck.
@@ -285,7 +290,7 @@
         new Camera2Interop.Extender<>(builder).setDeviceStateCallback(mDeviceStateCallback);
         ImageAnalysis useCase = builder.build();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase);
 
         verify(mDeviceStateCallback, timeout(3000)).onOpened(any(CameraDevice.class));
     }
@@ -296,7 +301,7 @@
         new Camera2Interop.Extender<>(builder).setDeviceStateCallback(mDeviceStateCallback);
         ImageAnalysis useCase = builder.build();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase);
 
         verify(mDeviceStateCallback, timeout(3000)).onOpened(any(CameraDevice.class));
     }
@@ -312,7 +317,7 @@
 
         ImageAnalysis useCase = builder.build();
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase);
 
         // When no analyzer is set, there will be no active surface for repeating request
         // CaptureSession#mSessionConfig will be null. Thus we wait until capture session
@@ -331,12 +336,11 @@
             new Camera2Interop.Extender<>(builder).setDeviceStateCallback(callback);
             ImageAnalysis useCase = builder.build();
 
-            CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                    DEFAULT_SELECTOR, useCase);
+            mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase);
 
             verify(callback, timeout(5000)).onOpened(any(CameraDevice.class));
 
-            camera.detachUseCases();
+            mCamera.detachUseCases();
 
             verify(callback, timeout(3000)).onClosed(any(CameraDevice.class));
         }
@@ -352,12 +356,11 @@
             new Camera2Interop.Extender<>(builder).setDeviceStateCallback(callback);
             ImageAnalysis useCase = builder.build();
 
-            CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                    DEFAULT_SELECTOR, useCase);
+            mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase);
 
             verify(callback, timeout(5000)).onOpened(any(CameraDevice.class));
 
-            camera.detachUseCases();
+            mCamera.detachUseCases();
 
             verify(callback, timeout(3000)).onClosed(any(CameraDevice.class));
         }
@@ -369,15 +372,14 @@
         new Camera2Interop.Extender<>(builder).setDeviceStateCallback(mDeviceStateCallback);
         ImageAnalysis useCase = builder.build();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                DEFAULT_SELECTOR, useCase);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase);
 
         verify(mDeviceStateCallback, timeout(3000)).onOpened(any(CameraDevice.class));
 
         // TODO(b/160249108) move off of main thread once UseCases can be attached on any
         //  thread
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
-                camera.removeUseCases(Collections.singletonList(useCase))
+                mCamera.removeUseCases(Collections.singletonList(useCase))
         );
         verify(mDeviceStateCallback, timeout(3000)).onClosed(any(CameraDevice.class));
     }
@@ -392,15 +394,15 @@
                 .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
                 .build();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                DEFAULT_SELECTOR, useCase0, useCase1);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase0,
+                useCase1);
 
         verify(mDeviceStateCallback, timeout(3000)).onOpened(any(CameraDevice.class));
 
         // TODO(b/160249108) move off of main thread once UseCases can be attached on any
         //  thread
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
-                camera.removeUseCases(Collections.singletonList(useCase1))
+                mCamera.removeUseCases(Collections.singletonList(useCase1))
         );
         Thread.sleep(3000);
 
@@ -417,16 +419,16 @@
                 .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
                 .build();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                DEFAULT_SELECTOR, useCase0, useCase1);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, DEFAULT_SELECTOR, useCase0,
+                useCase1);
 
         verify(mDeviceStateCallback, timeout(3000)).onOpened(any(CameraDevice.class));
 
         // TODO(b/160249108) move off of main thread once UseCases can be attached on any
         //  thread
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-                    camera.removeUseCases(Collections.singleton(useCase0));
-                    camera.removeUseCases(Collections.singleton(useCase1));
+                    mCamera.removeUseCases(Collections.singleton(useCase0));
+                    mCamera.removeUseCases(Collections.singleton(useCase1));
                 }
         );
 
@@ -443,15 +445,14 @@
         new Camera2Interop.Extender<>(builder1).setDeviceStateCallback(mDeviceStateCallback);
         ImageCapture imageCapture = builder1.build();
 
-        CameraUseCaseAdapter camera = CameraUtil.getCameraUseCaseAdapter(mContext,
-                DEFAULT_SELECTOR);
+        mCamera = CameraUtil.createCameraUseCaseAdapter(mContext, DEFAULT_SELECTOR);
 
         // TODO(b/160249108) move off of main thread once UseCases can be attached on any
         //  thread
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             try {
-                camera.addUseCases(Collections.singleton(imageCapture));
-                camera.addUseCases(Collections.singleton(imageAnalysis));
+                mCamera.addUseCases(Collections.singleton(imageCapture));
+                mCamera.addUseCases(Collections.singleton(imageAnalysis));
             } catch (CameraUseCaseAdapter.CameraException e) {
                 throw new IllegalArgumentException(e);
             }
@@ -463,8 +464,8 @@
         // TODO(b/160249108) move off of main thread once UseCases can be attached on any
         //  thread
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            camera.removeUseCases(Collections.singleton(imageCapture));
-            camera.removeUseCases(Collections.singleton(imageAnalysis));
+            mCamera.removeUseCases(Collections.singleton(imageCapture));
+            mCamera.removeUseCases(Collections.singleton(imageAnalysis));
         });
 
         verify(mDeviceStateCallback, timeout(3000)).onClosed(any(CameraDevice.class));
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/TorchControlDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/TorchControlDeviceTest.java
index efcf52f..fe8afe9 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/TorchControlDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/TorchControlDeviceTest.java
@@ -32,6 +32,7 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.GrantPermissionRule;
 
 import com.google.common.util.concurrent.ListenableFuture;
@@ -57,6 +58,7 @@
             GrantPermissionRule.grant(android.Manifest.permission.CAMERA);
 
     private TorchControl mTorchControl;
+    private CameraUseCaseAdapter mCamera;
 
     @Before
     public void setUp() {
@@ -76,15 +78,23 @@
         ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().build();
         // Make ImageAnalysis active.
         imageAnalysis.setAnalyzer(CameraXExecutors.mainThreadExecutor(), ImageProxy::close);
-        CameraUseCaseAdapter cameraUseCaseAdapter = CameraUtil.getCameraAndAttachUseCase(context,
-                cameraSelector, imageAnalysis);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(context, cameraSelector, imageAnalysis);
         Camera2CameraControlImpl cameraControl = (Camera2CameraControlImpl)
-                cameraUseCaseAdapter.getCameraControlInternal();
+                mCamera.getCameraControl();
+
         mTorchControl = cameraControl.getTorchControl();
     }
 
     @After
     public void tearDown() throws ExecutionException, InterruptedException, TimeoutException {
+        if (mCamera != null) {
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
+                    //TODO: The removeUseCases() call might be removed after clarifying the
+                    // abortCaptures() issue in b/162314023.
+                    mCamera.removeUseCases(mCamera.getUseCases())
+            );
+        }
+
         CameraX.shutdown().get(10000, TimeUnit.MILLISECONDS);
     }
 
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2InteropDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2InteropDeviceTest.java
index 09f7f8b..c34f231 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2InteropDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/interop/Camera2InteropDeviceTest.java
@@ -73,6 +73,7 @@
     private CameraCaptureSession.CaptureCallback mMockCaptureCallback =
             mock(CameraCaptureSession.CaptureCallback.class);
     private Context mContext;
+    private CameraUseCaseAdapter mCamera;
 
     @Rule
     public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(
@@ -90,6 +91,14 @@
 
     @After
     public void tearDown() throws ExecutionException, InterruptedException, TimeoutException {
+        if (mCamera != null) {
+            mInstrumentation.runOnMainSync(() ->
+                    //TODO: The removeUseCases() call might be removed after clarifying the
+                    // abortCaptures() issue in b/162314023.
+                    mCamera.removeUseCases(mCamera.getUseCases())
+            );
+        }
+
         CameraX.shutdown().get(10000, TimeUnit.MILLISECONDS);
     }
 
@@ -110,7 +119,7 @@
         imageAnalysis.setAnalyzer(CameraXExecutors.highPriorityExecutor(),
                 mock(ImageAnalysis.Analyzer.class));
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, imageAnalysis);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, imageAnalysis);
 
         verify(mMockCaptureCallback, timeout(5000).atLeastOnce()).onCaptureCompleted(
                 any(CameraCaptureSession.class),
@@ -222,13 +231,13 @@
     private Rect getZoom2XCropRegion() throws Exception {
         AtomicReference<String> cameraIdRef = new AtomicReference<>();
         ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().build();
-        CameraUseCaseAdapter camera = CameraUtil.getCameraAndAttachUseCase(mContext,
-                mCameraSelector, imageAnalysis);
-        String cameraId = camera.getCameraInfoInternal().getCameraId();
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, imageAnalysis);
+
+        String cameraId = Camera2CameraInfo.fromCameraInfo(mCamera.getCameraInfo()).getCameraId();
         cameraIdRef.set(cameraId);
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
-                camera.removeUseCases(Collections.singleton(imageAnalysis))
+                mCamera.removeUseCases(Collections.singleton(imageAnalysis))
         );
 
         CameraManager cameraManager =
@@ -260,7 +269,7 @@
         imageAnalysis.setAnalyzer(CameraXExecutors.highPriorityExecutor(),
                 mock(ImageAnalysis.Analyzer.class));
 
-        CameraUtil.getCameraAndAttachUseCase(mContext, mCameraSelector, imageAnalysis);
+        mCamera = CameraUtil.createCameraAndAttachUseCase(mContext, mCameraSelector, imageAnalysis);
     }
 
     private <T> void verifyCaptureRequestParameter(
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
index 6ae5131..f79534c 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
@@ -37,8 +37,6 @@
 import androidx.camera.camera2.internal.annotation.CameraExecutor;
 import androidx.camera.camera2.internal.compat.CameraAccessExceptionCompat;
 import androidx.camera.camera2.internal.compat.CameraManagerCompat;
-import androidx.camera.core.CameraControl;
-import androidx.camera.core.CameraInfo;
 import androidx.camera.core.CameraUnavailableException;
 import androidx.camera.core.Preview;
 import androidx.camera.core.UseCase;
@@ -1143,18 +1141,6 @@
         }
     }
 
-    @NonNull
-    @Override
-    public CameraControl getCameraControl() {
-        return getCameraControlInternal();
-    }
-
-    @NonNull
-    @Override
-    public CameraInfo getCameraInfo() {
-        return getCameraInfoInternal();
-    }
-
     enum InternalState {
         /**
          * Stable state once the camera has been constructed.
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraInfo.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraInfo.java
index adb34b6..2b3bb82 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraInfo.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/interop/Camera2CameraInfo.java
@@ -64,9 +64,19 @@
      * Gets the string camera ID.
      *
      * <p>The camera ID is the same as the camera ID that would be obtained from
-     * {@link android.hardware.camera2.CameraManager#getCameraIdList()}.
+     * {@link android.hardware.camera2.CameraManager#getCameraIdList()}. The ID that is retrieved
+     * is not static and can change depending on the current internal configuration of the
+     * {@link androidx.camera.core.Camera} from which the CameraInfo was retrieved.
+     *
+     * The Camera is a logical camera which can be backed by multiple
+     * {@link android.hardware.camera2.CameraDevice}. However, only one CameraDevice is active at
+     * one time. When the CameraDevice changes then the camera id will change.
      *
      * @return the camera ID.
+
+     * @throws IllegalStateException if the camera info does not contain the camera 2 camera ID
+     *                               (e.g., if CameraX was not initialized with a
+     *                               {@link androidx.camera.camera2.Camera2Config}).
      */
     @NonNull
     public String getCameraId() {
@@ -88,4 +98,30 @@
     public <T> T getCameraCharacteristic(@NonNull CameraCharacteristics.Key<T> key) {
         return mCamera2CameraInfoImpl.getCameraCharacteristics().get(key);
     }
+
+    /**
+     * Returns the {@link CameraCharacteristics} for this camera.
+     *
+     * <p>The CameraCharacteristics will be the ones that would be obtained by
+     * {@link android.hardware.camera2.CameraManager#getCameraCharacteristics(String)}. The
+     * CameraCharacteristics that are retrieved are not static and can change depending on the
+     * current internal configuration of the camera.
+     *
+     * @param cameraInfo The {@link CameraInfo} to extract the CameraCharacteristics from.
+     * @throws IllegalStateException if the camera info does not contain the camera 2
+     *                               characteristics(e.g., if CameraX was not initialized with a
+     *                               {@link androidx.camera.camera2.Camera2Config}).
+     *
+     * @hide
+     */
+    // TODO: Hidden until new extensions API released.
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @NonNull
+    public static CameraCharacteristics extractCameraCharacteristics(
+            @NonNull CameraInfo cameraInfo) {
+        Preconditions.checkState(cameraInfo instanceof Camera2CameraInfoImpl, "CameraInfo does "
+                + "not contain any Camera2 information.");
+        Camera2CameraInfoImpl impl = (Camera2CameraInfoImpl) cameraInfo;
+        return impl.getCameraCharacteristics();
+    }
 }
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
index a61b30c..a71184d 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
@@ -536,7 +536,7 @@
                 .setTargetAspectRatio(AspectRatio.RATIO_16_9)
                 .build();
 
-        CameraUseCaseAdapter cameraUseCaseAdapter = CameraUtil.getCameraUseCaseAdapter(mContext,
+        CameraUseCaseAdapter cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(mContext,
                 CameraSelector.DEFAULT_BACK_CAMERA);
 
         cameraUseCaseAdapter.addUseCases(Arrays.asList(preview, imageCapture, imageAnalysis));
@@ -589,7 +589,7 @@
         // Preview/ImageCapture/ImageAnalysis' default config settings that will be applied after
         // bound to lifecycle. Calling bindToLifecycle here to make sure sizes matching to
         // default aspect ratio will be selected.
-        CameraUseCaseAdapter cameraUseCaseAdapter = CameraUtil.getCameraUseCaseAdapter(mContext,
+        CameraUseCaseAdapter cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(mContext,
                 CameraSelector.DEFAULT_BACK_CAMERA);
 
         cameraUseCaseAdapter.addUseCases(Arrays.asList(preview,
diff --git a/camera/camera-core/api/public_plus_experimental_1.0.0-beta09.txt b/camera/camera-core/api/public_plus_experimental_1.0.0-beta09.txt
index ff65585..7da2353 100644
--- a/camera/camera-core/api/public_plus_experimental_1.0.0-beta09.txt
+++ b/camera/camera-core/api/public_plus_experimental_1.0.0-beta09.txt
@@ -340,7 +340,7 @@
 
   @androidx.camera.core.ExperimentalUseCaseGroup public final class UseCaseGroup {
     method public java.util.List<androidx.camera.core.UseCase!> getUseCases();
-    method public androidx.camera.core.ViewPort getViewPort();
+    method public androidx.camera.core.ViewPort? getViewPort();
   }
 
   @androidx.camera.core.ExperimentalUseCaseGroup public static final class UseCaseGroup.Builder {
diff --git a/camera/camera-core/api/public_plus_experimental_current.txt b/camera/camera-core/api/public_plus_experimental_current.txt
index ff65585..7da2353 100644
--- a/camera/camera-core/api/public_plus_experimental_current.txt
+++ b/camera/camera-core/api/public_plus_experimental_current.txt
@@ -340,7 +340,7 @@
 
   @androidx.camera.core.ExperimentalUseCaseGroup public final class UseCaseGroup {
     method public java.util.List<androidx.camera.core.UseCase!> getUseCases();
-    method public androidx.camera.core.ViewPort getViewPort();
+    method public androidx.camera.core.ViewPort? getViewPort();
   }
 
   @androidx.camera.core.ExperimentalUseCaseGroup public static final class UseCaseGroup.Builder {
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
index 31cfda7..e9d4858 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/ImageCaptureTest.java
@@ -103,7 +103,7 @@
         ImageCapture.OnImageCapturedCallback callback = mock(
                 ImageCapture.OnImageCapturedCallback.class);
         FakeCameraControl fakeCameraControl =
-                ((FakeCameraControl) mCameraUseCaseAdapter.getCameraControlInternal());
+                ((FakeCameraControl) mCameraUseCaseAdapter.getCameraControl());
 
         fakeCameraControl.setOnNewCaptureRequestListener(captureConfigs -> {
             // Notify the cancel after the capture request has been successfully submitted
@@ -134,7 +134,7 @@
         ImageCapture.OnImageCapturedCallback callback = mock(
                 ImageCapture.OnImageCapturedCallback.class);
         FakeCameraControl fakeCameraControl =
-                ((FakeCameraControl) mCameraUseCaseAdapter.getCameraControlInternal());
+                ((FakeCameraControl) mCameraUseCaseAdapter.getCameraControl());
         fakeCameraControl.setOnNewCaptureRequestListener(captureConfigs -> {
             // Notify the failure after the capture request has been successfully submitted
             fakeCameraControl.notifyAllRequestsOnCaptureFailed();
@@ -164,8 +164,7 @@
             }
         });
         FakeCameraControl fakeCameraControl =
-                ((FakeCameraControl) mCameraUseCaseAdapter.getCameraControlInternal());
-
+                ((FakeCameraControl) mCameraUseCaseAdapter.getCameraControl());
         FakeCameraControl.OnNewCaptureRequestListener mockCaptureRequestListener =
                 mock(FakeCameraControl.OnNewCaptureRequestListener.class);
         fakeCameraControl.setOnNewCaptureRequestListener(mockCaptureRequestListener);
@@ -198,7 +197,7 @@
         ImageCapture.OnImageCapturedCallback callback = mock(
                 ImageCapture.OnImageCapturedCallback.class);
         FakeCameraControl fakeCameraControl =
-                ((FakeCameraControl) mCameraUseCaseAdapter.getCameraControlInternal());
+                ((FakeCameraControl) mCameraUseCaseAdapter.getCameraControl());
         CountDownLatch latch = new CountDownLatch(1);
         fakeCameraControl.setOnNewCaptureRequestListener(captureConfigs -> {
             latch.countDown();
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/SurfaceRequestTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/SurfaceRequestTest.java
index 84eb46e..334bf85 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/SurfaceRequestTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/SurfaceRequestTest.java
@@ -28,6 +28,7 @@
 import android.view.Surface;
 
 import androidx.annotation.NonNull;
+import androidx.camera.core.impl.DeferrableSurface;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.testing.fakes.FakeCamera;
 import androidx.core.content.ContextCompat;
@@ -40,8 +41,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicReference;
 
 @SmallTest
@@ -53,8 +57,26 @@
             SurfaceRequest.TransformationInfo.of(new Rect(), 0, Surface.ROTATION_0);
     private static final Consumer<SurfaceRequest.Result> NO_OP_RESULT_LISTENER = ignored -> {
     };
+    private static final long FINALIZE_TIMEOUT_MILLIS = 200L;
+    private static final int NUM_GC_ITERATIONS = 10;
     private static final Surface MOCK_SURFACE = mock(Surface.class);
-    private List<SurfaceRequest> mSurfaceRequests = new ArrayList<>();
+    private final List<SurfaceRequest> mSurfaceRequests = new ArrayList<>();
+
+    private static void runFinalization() throws TimeoutException, InterruptedException {
+        ReferenceQueue<Object> finalizeAwaitQueue = new ReferenceQueue<>();
+        PhantomReference<Object> finalizeSignal;
+        // Ensure finalization occurs multiple times
+        for (int i = 0; i < NUM_GC_ITERATIONS; ++i) {
+            finalizeSignal = new PhantomReference<>(new Object(), finalizeAwaitQueue);
+            Runtime.getRuntime().gc();
+            Runtime.getRuntime().runFinalization();
+            if (finalizeAwaitQueue.remove(FINALIZE_TIMEOUT_MILLIS) == null) {
+                throw new TimeoutException(
+                        "Finalization failed on iteration " + (i + 1) + " of " + NUM_GC_ITERATIONS);
+            }
+            finalizeSignal.clear();
+        }
+    }
 
     @After
     public void tearDown() {
@@ -222,9 +244,80 @@
         assertThat(infoReference.get()).isEqualTo(FAKE_INFO);
     }
 
+    @SuppressWarnings("UnusedAssignment") // request assigned to null to make GC eligible
+    @Test
+    public void deferrableSurface_stronglyReferencesSurfaceRequest()
+            throws TimeoutException, InterruptedException {
+        // Arrange.
+        SurfaceRequest request = createNewRequestWithoutAutoCleanup(FAKE_SIZE);
+        // Retrieve the DeferrableSurface which should maintain the strong reference to the
+        // SurfaceRequest
+        DeferrableSurface deferrableSurface = request.getDeferrableSurface();
+        ReferenceQueue<SurfaceRequest> referenceQueue = new ReferenceQueue<>();
+        // Ensure surface request garbage collection is tracked
+        PhantomReference<SurfaceRequest> phantomReference = new PhantomReference<>(request,
+                referenceQueue);
+        try {
+            // Act.
+            // Null out the original reference to the SurfaceRequest. DeferrableSurface should be
+            // the only reference remaining.
+            request = null;
+            runFinalization();
+            boolean requestFinalized = (referenceQueue.poll() != null);
+
+            // Assert.
+            assertThat(requestFinalized).isFalse();
+        } finally {
+            // Clean up
+            phantomReference.clear();
+            deferrableSurface.close();
+        }
+    }
+
+    @SuppressWarnings("UnusedAssignment") // deferrableSurface assigned to null to make GC eligible
+    @Test
+    public void surfaceRequest_stronglyReferencesDeferrableSurface()
+            throws TimeoutException, InterruptedException {
+        // Arrange.
+        SurfaceRequest request = createNewRequestWithoutAutoCleanup(FAKE_SIZE);
+        // Retrieve the DeferrableSurface which should maintain the strong reference to the
+        // SurfaceRequest
+        DeferrableSurface deferrableSurface = request.getDeferrableSurface();
+        ReferenceQueue<DeferrableSurface> referenceQueue = new ReferenceQueue<>();
+        // Ensure surface request garbage collection is tracked
+        PhantomReference<DeferrableSurface> phantomReference = new PhantomReference<>(
+                deferrableSurface, referenceQueue);
+        try {
+            // Act.
+            // Null out the original reference to the DeferrableSurface. SurfaceRequest should be
+            // the only reference remaining.
+            deferrableSurface = null;
+            runFinalization();
+            boolean deferrableSurfaceFinalized = (referenceQueue.poll() != null);
+
+            // Assert.
+            assertThat(deferrableSurfaceFinalized).isFalse();
+        } finally {
+            // Clean up
+            phantomReference.clear();
+            request.getDeferrableSurface().close();
+        }
+    }
+
+    // The test method is responsible for ensuring that the SurfaceRequest is finished.
+    private SurfaceRequest createNewRequestWithoutAutoCleanup(@NonNull Size size) {
+        return createNewRequest(size, /*autoCleanUp=*/false);
+    }
+
     private SurfaceRequest createNewRequest(@NonNull Size size) {
+        return createNewRequest(size, /*autoCleanUp=*/true);
+    }
+
+    private SurfaceRequest createNewRequest(@NonNull Size size, boolean autoCleanup) {
         SurfaceRequest request = new SurfaceRequest(size, new FakeCamera(), false);
-        mSurfaceRequests.add(request);
+        if (autoCleanup) {
+            mSurfaceRequests.add(request);
+        }
         return request;
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Camera.java b/camera/camera-core/src/main/java/androidx/camera/core/Camera.java
index a9f855d..dea0fb3 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Camera.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Camera.java
@@ -17,9 +17,13 @@
 package androidx.camera.core;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.camera.core.impl.CameraInternal;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
+import java.util.Collection;
+
 /**
  * The camera interface is used to control the flow of data to use cases, control the
  * camera via the {@link CameraControl}, and publish the state of the camera via {@link CameraInfo}.
@@ -57,4 +61,21 @@
      */
     @NonNull
     CameraInfo getCameraInfo();
+
+    /**
+     * Returns all of the {@link CameraInternal} instances represented by this Camera.
+     *
+     * <p> A {@link Camera} is a logical camera which wraps one or more {@link CameraInternal}.
+     * At any time, only one of the CameraInternal is actually being used, and it is up to the
+     * implementation to determine which {@link CameraInternal} will be used. Certain
+     * reconfigurations of the camera will cause the current CameraInternal camera to change.
+     * However, it will be transparent to the {@link CameraControl} and {@link CameraInfo}
+     * retrieved from {@link #getCameraControl()} and {@link #getCameraInfo()}.
+     *
+     * <p> The set of CameraInternal should be static for the lifetime of the Camera.
+     * @hide
+     */
+    @NonNull
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    Collection<CameraInternal> getCameraInternals();
 }
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 e84615c..8753394 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
@@ -579,7 +579,7 @@
                 CameraThreadConfig cameraThreadConfig = CameraThreadConfig.create(mCameraExecutor,
                         mSchedulerHandler);
 
-                mCameraFactory = cameraFactoryProvider.newInstance(context,
+                mCameraFactory = cameraFactoryProvider.newInstance(mAppContext,
                         cameraThreadConfig);
                 CameraDeviceSurfaceManager.Provider surfaceManagerProvider =
                         mCameraXConfig.getDeviceSurfaceManagerProvider(null);
@@ -588,7 +588,7 @@
                             "Invalid app configuration provided. Missing "
                                     + "CameraDeviceSurfaceManager."));
                 }
-                mSurfaceManager = surfaceManagerProvider.newInstance(context);
+                mSurfaceManager = surfaceManagerProvider.newInstance(mAppContext);
 
                 UseCaseConfigFactory.Provider configFactoryProvider =
                         mCameraXConfig.getUseCaseConfigFactoryProvider(null);
@@ -597,7 +597,7 @@
                             "Invalid app configuration provided. Missing "
                                     + "UseCaseConfigFactory."));
                 }
-                mDefaultConfigFactory = configFactoryProvider.newInstance(context);
+                mDefaultConfigFactory = configFactoryProvider.newInstance(mAppContext);
 
                 if (cameraExecutor instanceof CameraExecutor) {
                     CameraExecutor executor = (CameraExecutor) cameraExecutor;
@@ -615,7 +615,7 @@
                     Logger.w(TAG, "Retry init. Start time " + startMs + " current time "
                             + SystemClock.elapsedRealtime(), e);
                     HandlerCompat.postDelayed(mSchedulerHandler, () -> initAndRetryRecursively(
-                            cameraExecutor, startMs, context, completer), RETRY_TOKEN,
+                            cameraExecutor, startMs, mAppContext, completer), RETRY_TOKEN,
                             RETRY_SLEEP_MILLIS);
 
                 } else {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index 5d01749..8a3b70b 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -373,7 +373,7 @@
             } else {
                 // No pending SurfaceRequest. It could be a previous request has already been
                 // sent, which means the caller wants to replace the Surface. Or, it could be the
-                // pipeline has not started.
+                // pipeline has not started. Or the use case may have been detached from the camera.
                 // Either way, try updating session config and let createPipeline() sends a
                 // new SurfaceRequest.
                 if (getAttachedSurfaceResolution() != null) {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/SurfaceRequest.java b/camera/camera-core/src/main/java/androidx/camera/core/SurfaceRequest.java
index 93b5d70..38ac668 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/SurfaceRequest.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/SurfaceRequest.java
@@ -31,6 +31,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
+import androidx.camera.core.impl.CameraInternal;
 import androidx.camera.core.impl.DeferrableSurface;
 import androidx.camera.core.impl.ImageOutputConfig;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
@@ -61,9 +62,10 @@
 public final class SurfaceRequest {
 
     private final Size mResolution;
-    private final Camera mCamera;
     private final boolean mRGBA8888Required;
 
+    private final CameraInternal mCamera;
+
     // For the camera to retrieve the surface from the user
     @SuppressWarnings("WeakerAccess") /*synthetic accessor */
     final ListenableFuture<Surface> mSurfaceFuture;
@@ -96,7 +98,7 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public SurfaceRequest(
             @NonNull Size resolution,
-            @NonNull Camera camera,
+            @NonNull CameraInternal camera,
             boolean isRGBA8888Required) {
         super();
         mResolution = resolution;
@@ -167,7 +169,10 @@
         mSurfaceCompleter = Preconditions.checkNotNull(surfaceCompleterRef.get());
 
         // Create the deferrable surface which will be used for communicating when the
-        // camera and consumer are done using the surface.
+        // camera and consumer are done using the surface. Note this anonymous inner class holds
+        // an implicit reference to the SurfaceRequest. This is by design, and ensures the
+        // SurfaceRequest and all contained future completers will not be garbage collected as
+        // long as the DeferrableSurface is referenced externally (via getDeferrableSurface()).
         mInternalDeferrableSurface = new DeferrableSurface() {
             @NonNull
             @Override
@@ -250,7 +255,7 @@
      */
     @NonNull
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public Camera getCamera() {
+    public CameraInternal getCamera() {
         return mCamera;
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/UseCaseGroup.java b/camera/camera-core/src/main/java/androidx/camera/core/UseCaseGroup.java
index 689261b..21d0c61 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/UseCaseGroup.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/UseCaseGroup.java
@@ -17,6 +17,7 @@
 package androidx.camera.core;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.core.util.Preconditions;
 import androidx.lifecycle.Lifecycle;
 
@@ -34,11 +35,13 @@
 @ExperimentalUseCaseGroup
 public final class UseCaseGroup {
 
-    private ViewPort mViewPort;
+    @Nullable
+    private final ViewPort mViewPort;
 
-    private List<UseCase> mUseCases;
+    @NonNull
+    private final List<UseCase> mUseCases;
 
-    UseCaseGroup(@NonNull ViewPort viewPort, @NonNull List<UseCase> useCases) {
+    UseCaseGroup(@Nullable ViewPort viewPort, @NonNull List<UseCase> useCases) {
         mViewPort = viewPort;
         mUseCases = useCases;
     }
@@ -46,7 +49,7 @@
     /**
      * Gets the {@link ViewPort} shared by the {@link UseCase} collection.
      */
-    @NonNull
+    @Nullable
     public ViewPort getViewPort() {
         return mViewPort;
     }
@@ -67,7 +70,7 @@
 
         private ViewPort mViewPort;
 
-        private List<UseCase> mUseCases;
+        private final List<UseCase> mUseCases;
 
         public Builder() {
             mUseCases = new ArrayList<>();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java
index 4bdfd0c..e163dd7 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java
@@ -18,14 +18,19 @@
 
 import androidx.annotation.NonNull;
 import androidx.camera.core.Camera;
+import androidx.camera.core.CameraControl;
+import androidx.camera.core.CameraInfo;
 import androidx.camera.core.UseCase;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
 import java.util.Collection;
+import java.util.Collections;
 
 /**
  * The camera interface. It is controlled by the change of state in use cases.
+ *
+ * <p> It is a Camera instance backed by a single physical camera.
  */
 public interface CameraInternal extends Camera, UseCase.StateChangeCallback {
     /**
@@ -144,4 +149,28 @@
     /** Returns an interface to retrieve characteristics of the camera. */
     @NonNull
     CameraInfoInternal getCameraInfoInternal();
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    // Camera interface
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    @NonNull
+    @Override
+    default CameraControl getCameraControl() {
+        return getCameraControlInternal();
+    }
+
+    @NonNull
+    @Override
+    default CameraInfo getCameraInfo() {
+        return getCameraInfoInternal();
+    }
+
+    /**
+     * Always returns only itself since there is only ever one CameraInternal.
+     */
+    @NonNull
+    @Override
+    default Collection<CameraInternal> getCameraInternals() {
+        return Collections.singleton(this);
+    }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
index 4ebd8fe..fd60409 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
@@ -23,12 +23,13 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.experimental.UseExperimental;
+import androidx.camera.core.Camera;
+import androidx.camera.core.CameraControl;
+import androidx.camera.core.CameraInfo;
 import androidx.camera.core.Logger;
 import androidx.camera.core.UseCase;
 import androidx.camera.core.ViewPort;
-import androidx.camera.core.impl.CameraControlInternal;
 import androidx.camera.core.impl.CameraDeviceSurfaceManager;
-import androidx.camera.core.impl.CameraInfoInternal;
 import androidx.camera.core.impl.CameraInternal;
 import androidx.camera.core.impl.SurfaceConfig;
 import androidx.camera.core.impl.UseCaseConfig;
@@ -51,7 +52,7 @@
  * extensions in order to select the correct CameraInternal instance which has the required
  * camera id.
  */
-public final class CameraUseCaseAdapter {
+public final class CameraUseCaseAdapter implements Camera {
     private final CameraInternal mCameraInternal;
     private final LinkedHashSet<CameraInternal> mCameraInternals;
     private final CameraDeviceSurfaceManager mCameraDeviceSurfaceManager;
@@ -324,16 +325,6 @@
         return suggestedResolutions;
     }
 
-    @NonNull
-    public CameraInfoInternal getCameraInfoInternal() {
-        return mCameraInternal.getCameraInfoInternal();
-    }
-
-    @NonNull
-    public CameraControlInternal getCameraControlInternal() {
-        return mCameraInternal.getCameraControlInternal();
-    }
-
     /**
      * An identifier for a {@link CameraUseCaseAdapter}.
      *
@@ -381,4 +372,25 @@
             super(cause);
         }
     }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    // Camera interface
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    @NonNull
+    @Override
+    public CameraControl getCameraControl() {
+        return mCameraInternal.getCameraControlInternal();
+    }
+
+    @NonNull
+    @Override
+    public CameraInfo getCameraInfo() {
+        return mCameraInternal.getCameraInfoInternal();
+    }
+
+    @NonNull
+    @Override
+    public Collection<CameraInternal> getCameraInternals() {
+        return mCameraInternals;
+    }
 }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
index 7dacb3c..31d8cee 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
@@ -312,7 +312,7 @@
         );
 
         CameraUseCaseAdapter cameraUseCaseAdapter =
-                CameraUtil.getCameraUseCaseAdapter(ApplicationProvider.getApplicationContext(),
+                CameraUtil.createCameraUseCaseAdapter(ApplicationProvider.getApplicationContext(),
                         CameraSelector.DEFAULT_BACK_CAMERA);
         cameraUseCaseAdapter.setViewPort(viewPort);
         cameraUseCaseAdapter.addUseCases(Collections.singleton(mImageAnalysis));
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
index c4a470e..b38856c 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
@@ -71,6 +71,7 @@
 import java.util.concurrent.ExecutionException
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicReference
+import kotlin.jvm.Throws
 
 private const val MAX_IMAGES = 3
 
@@ -360,7 +361,7 @@
             .setSessionOptionUnpacker(sessionOptionUnpacker)
             .build()
 
-        cameraUseCaseAdapter = CameraUtil.getCameraUseCaseAdapter(ApplicationProvider
+        cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(ApplicationProvider
             .getApplicationContext<Context>(), CameraSelector.DEFAULT_BACK_CAMERA)
 
         cameraUseCaseAdapter.setViewPort(viewPort)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
index 3b36ab1..5483966 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
@@ -23,12 +23,12 @@
 import android.util.Rational
 import android.util.Size
 import android.view.Surface
-import android.view.Surface.ROTATION_0
 import androidx.camera.core.impl.CameraFactory
 import androidx.camera.core.impl.CameraThreadConfig
 import androidx.camera.core.impl.SessionConfig
 import androidx.camera.core.impl.UseCaseConfig
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.core.internal.CameraUseCaseAdapter
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.fakes.FakeAppConfig
 import androidx.camera.testing.fakes.FakeCamera
@@ -37,7 +37,6 @@
 import androidx.camera.testing.fakes.FakeUseCase
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
@@ -49,6 +48,9 @@
 import org.robolectric.annotation.internal.DoNotInstrument
 import java.util.Collections
 import java.util.concurrent.ExecutionException
+import kotlin.jvm.Throws
+
+private val TEST_CAMERA_SELECTOR = CameraSelector.DEFAULT_BACK_CAMERA
 
 /**
  * Unit tests for [Preview].
@@ -61,6 +63,8 @@
 )
 class PreviewTest {
 
+    var cameraUseCaseAdapter: CameraUseCaseAdapter? = null
+
     @Before
     @Throws(ExecutionException::class, InterruptedException::class)
     fun setUp() {
@@ -82,6 +86,10 @@
     @After
     @Throws(ExecutionException::class, InterruptedException::class)
     fun tearDown() {
+        with (cameraUseCaseAdapter) {
+            this?.removeUseCases(useCases)
+        }
+        cameraUseCaseAdapter = null
         CameraX.shutdown().get()
     }
 
@@ -149,16 +157,15 @@
         val sessionOptionUnpacker =
             { _: UseCaseConfig<*>?, _: SessionConfig.Builder? -> }
         val preview = Preview.Builder()
-            .setTargetRotation(ROTATION_0)
+            .setTargetRotation(Surface.ROTATION_0)
             .setSessionOptionUnpacker(sessionOptionUnpacker)
             .build()
-        val cameraUseCaseAdapter = CameraUtil.getCameraUseCaseAdapter(
-            ApplicationProvider
-                .getApplicationContext<Context>(), CameraSelector.DEFAULT_BACK_CAMERA
+        cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(
+            ApplicationProvider.getApplicationContext(), TEST_CAMERA_SELECTOR
         )
         val rational1 = Rational(1, 1)
-        cameraUseCaseAdapter.setViewPort(ViewPort.Builder(rational1, ROTATION_0).build())
-        cameraUseCaseAdapter.addUseCases(Collections.singleton<UseCase>(preview))
+        cameraUseCaseAdapter!!.setViewPort(ViewPort.Builder(rational1, Surface.ROTATION_0).build())
+        cameraUseCaseAdapter!!.addUseCases(Collections.singleton<UseCase>(preview))
 
         // Set SurfaceProvider
         var receivedTransformationInfo: SurfaceRequest.TransformationInfo? = null
@@ -175,8 +182,8 @@
         // Act: bind another use case with a different viewport.
         val fakeUseCase = FakeUseCase()
         val rational2 = Rational(2, 1)
-        cameraUseCaseAdapter.setViewPort(ViewPort.Builder(rational2, ROTATION_0).build())
-        cameraUseCaseAdapter.addUseCases(listOf(preview, fakeUseCase))
+        cameraUseCaseAdapter!!.setViewPort(ViewPort.Builder(rational2, Surface.ROTATION_0).build())
+        cameraUseCaseAdapter!!.addUseCases(listOf(preview, fakeUseCase))
         shadowOf(getMainLooper()).idle()
 
         // Assert: received viewport's aspect ratio is the latest one.
@@ -193,13 +200,13 @@
         val sessionOptionUnpacker =
             { _: UseCaseConfig<*>?, _: SessionConfig.Builder? -> }
         val preview = Preview.Builder()
-            .setTargetRotation(ROTATION_0)
+            .setTargetRotation(Surface.ROTATION_0)
             .setSessionOptionUnpacker(sessionOptionUnpacker)
             .build()
-        val cameraUseCaseAdapter = CameraUtil.getCameraUseCaseAdapter(
-            ApplicationProvider.getApplicationContext(), CameraSelector.DEFAULT_BACK_CAMERA
+        cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(
+            ApplicationProvider.getApplicationContext(), TEST_CAMERA_SELECTOR
         )
-        cameraUseCaseAdapter.addUseCases(Collections.singleton<UseCase>(preview))
+        cameraUseCaseAdapter!!.addUseCases(Collections.singleton<UseCase>(preview))
         var receivedTransformationInfo: SurfaceRequest.TransformationInfo? = null
         preview.setSurfaceProvider { request ->
             request.setTransformationInfoListener(
@@ -228,9 +235,9 @@
             .setTargetRotation(Surface.ROTATION_0)
             .setSessionOptionUnpacker(sessionOptionUnpacker)
             .build()
-        val cameraUseCaseAdapter = CameraUtil.getCameraUseCaseAdapter(
+        val cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(
             ApplicationProvider
-                .getApplicationContext(), CameraSelector.DEFAULT_BACK_CAMERA
+                .getApplicationContext(), TEST_CAMERA_SELECTOR
         )
         cameraUseCaseAdapter.addUseCases(Collections.singleton<UseCase>(preview))
 
@@ -269,6 +276,42 @@
         assertThat(receivedSurfaceRequest).isNotSameInstanceAs(pendingSurfaceRequest)
     }
 
+    @Test
+    fun setSurfaceProviderAfterDetach_receivesSurfaceRequestAfterAttach() {
+        // Arrange: attach Preview without a SurfaceProvider.
+        val sessionOptionUnpacker =
+            { _: UseCaseConfig<*>?, _: SessionConfig.Builder? -> }
+        val preview = Preview.Builder()
+            .setTargetRotation(Surface.ROTATION_0)
+            .setSessionOptionUnpacker(sessionOptionUnpacker)
+            .build()
+        cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(
+            ApplicationProvider
+                .getApplicationContext(), TEST_CAMERA_SELECTOR
+        )
+        // Attach
+        cameraUseCaseAdapter!!.addUseCases(Collections.singleton<UseCase>(preview))
+        // Detach
+        cameraUseCaseAdapter!!.removeUseCases(Collections.singleton<UseCase>(preview))
+
+        // Act: set a SurfaceProvider after detaching
+        var receivedSurfaceRequest = false
+        preview.setSurfaceProvider { receivedSurfaceRequest = true }
+        shadowOf(getMainLooper()).idle()
+
+        val receivedWhileDetached = receivedSurfaceRequest
+
+        // Attach
+        cameraUseCaseAdapter!!.addUseCases(Collections.singleton<UseCase>(preview))
+        shadowOf(getMainLooper()).idle()
+
+        val receivedAfterAttach = receivedSurfaceRequest
+
+        // Assert: received a SurfaceRequest.
+        assertThat(receivedWhileDetached).isFalse()
+        assertThat(receivedAfterAttach).isTrue()
+    }
+
     private fun bindToLifecycleAndGetSurfaceRequest(): SurfaceRequest {
         return bindToLifecycleAndGetResult(null).first
     }
@@ -299,14 +342,12 @@
         }
 
         // Act.
-        val cameraUseCaseAdapter = CameraUtil.getCameraUseCaseAdapter(
-            ApplicationProvider
-                .getApplicationContext<Context>(), CameraSelector.DEFAULT_BACK_CAMERA
+        cameraUseCaseAdapter = CameraUtil.createCameraUseCaseAdapter(
+            ApplicationProvider.getApplicationContext(), TEST_CAMERA_SELECTOR
         )
-
-        cameraUseCaseAdapter.setViewPort(viewPort)
-        cameraUseCaseAdapter.addUseCases(Collections.singleton<UseCase>(preview))
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+        cameraUseCaseAdapter!!.setViewPort(viewPort)
+        cameraUseCaseAdapter!!.addUseCases(Collections.singleton<UseCase>(preview))
+        shadowOf(getMainLooper()).idle()
         return Pair(surfaceRequest!!, transformationInfo!!)
     }
 }
\ No newline at end of file
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
index dd497ae..1f52190 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionTest.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionTest.java
@@ -93,12 +93,17 @@
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     private final Context mContext = ApplicationProvider.getApplicationContext();
 
-    private EffectMode mEffectMode;
+    private final EffectMode mEffectMode;
+    @Extensions.ExtensionMode
+    private final int mExtensionMode;
     @CameraSelector.LensFacing
-    private int mLensFacing;
+    private final int mLensFacing;
+
+    private CameraUseCaseAdapter mCamera;
 
     public ExtensionTest(EffectMode effectMode, @CameraSelector.LensFacing int lensFacing) {
         mEffectMode = effectMode;
+        mExtensionMode = ExtensionsTestUtil.effectModeToExtensionMode(mEffectMode);
         mLensFacing = lensFacing;
     }
 
@@ -111,8 +116,12 @@
 
         assumeTrue(CameraUtil.hasCameraWithLensFacing(mLensFacing));
         assumeTrue(ExtensionsTestUtil.initExtensions(mContext));
-        assumeTrue(ExtensionsManager.isExtensionAvailable(mEffectMode, mLensFacing));
         assumeTrue(isTargetDeviceAvailableForExtensions(mLensFacing));
+        CameraSelector cameraSelector =
+                new CameraSelector.Builder().requireLensFacing(mLensFacing).build();
+        mCamera = CameraUtil.createCameraUseCaseAdapter(mContext, cameraSelector);
+        Extensions extensions = ExtensionsManager.getExtensions(mContext);
+        assumeTrue(extensions.isExtensionAvailable(mCamera, mExtensionMode));
     }
 
     @After
@@ -123,6 +132,8 @@
 
     @Test
     public void testCanBindToLifeCycleAndTakePicture() {
+
+
         ImageCapture.OnImageCapturedCallback mockOnImageCapturedCallback = mock(
                 ImageCapture.OnImageCapturedCallback.class);
 
@@ -131,10 +142,7 @@
                 mLensFacing);
         Preview preview = ExtensionsTestUtil.createPreviewWithEffect(mEffectMode, mLensFacing);
 
-        CameraSelector cameraSelector =
-                new CameraSelector.Builder().requireLensFacing(mLensFacing).build();
-        CameraUseCaseAdapter cameraUseCaseAdapter =
-                CameraUtil.getCameraUseCaseAdapter(mContext, cameraSelector);
+
         mInstrumentation.runOnMainSync(
                 () -> {
                     // To set the update listener and Preview will change to active state.
@@ -155,7 +163,7 @@
                             }));
 
                     try {
-                        cameraUseCaseAdapter.addUseCases(Arrays.asList(preview, imageCapture));
+                        mCamera.addUseCases(Arrays.asList(preview, imageCapture));
                     } catch (CameraUseCaseAdapter.CameraException e) {
                         throw new IllegalArgumentException("Unable to bind preview and image "
                                 + "capture");
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsTest.kt
new file mode 100644
index 0000000..b5557095
--- /dev/null
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.camera.extensions
+
+import android.content.Context
+import android.os.Build
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.CameraSelector.LensFacing
+import androidx.camera.core.CameraX
+import androidx.camera.extensions.Extensions.ExtensionMode
+import androidx.camera.extensions.ExtensionsManager.EffectMode
+import androidx.camera.extensions.util.ExtensionsTestUtil
+import androidx.camera.testing.CameraUtil
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+import kotlin.jvm.Throws
+
+@MediumTest
+@RunWith(Parameterized::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+class ExtensionsTest(
+    @field:ExtensionMode @param:ExtensionMode private val mExtensionMode: Int,
+    @field:LensFacing @param:LensFacing private val mLensFacing: Int
+) {
+    private val mContext =
+        ApplicationProvider.getApplicationContext<Context>()
+
+    private val mEffectMode: EffectMode =
+        ExtensionsTestUtil.extensionModeToEffectMode(mExtensionMode)
+
+    private lateinit var mExtensions: Extensions
+
+    @Before
+    @Throws(Exception::class)
+    fun setUp() {
+        assumeTrue(CameraUtil.deviceHasCamera())
+        val cameraXConfig = Camera2Config.defaultConfig()
+        CameraX.initialize(mContext, cameraXConfig).get()
+        assumeTrue(
+            CameraUtil.hasCameraWithLensFacing(
+                mLensFacing
+            )
+        )
+        assumeTrue(ExtensionsTestUtil.initExtensions(mContext))
+        mExtensions = ExtensionsManager.getExtensions(mContext)
+    }
+
+    @After
+    @Throws(
+        InterruptedException::class,
+        ExecutionException::class,
+        TimeoutException::class
+    )
+    fun cleanUp() {
+        CameraX.shutdown()[10000, TimeUnit.MILLISECONDS]
+        ExtensionsManager.deinit().get()
+    }
+
+    companion object {
+        @JvmStatic
+        @get:Parameterized.Parameters(name = "extension = {0}, facing = {1}")
+        val parameters: Collection<Array<Any>>
+            get() = ExtensionsTestUtil.getAllExtensionsLensFacingCombinations()
+    }
+
+    // TODO: Can be removed after the Extensions class is fully implemented.
+    @Test
+    fun isExtensionAvailable() {
+        val cameraSelector = CameraSelector.Builder().requireLensFacing(mLensFacing).build()
+        val camera = CameraUtil.createCameraUseCaseAdapter(mContext, cameraSelector)
+
+        assertThat(
+            ExtensionsManager.isExtensionAvailable(mEffectMode, mLensFacing) ==
+                    mExtensions.isExtensionAvailable(camera, mExtensionMode)
+        ).isTrue()
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureExtenderTest.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureExtenderTest.java
index 93fd396..12ffc3e 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureExtenderTest.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageCaptureExtenderTest.java
@@ -129,7 +129,7 @@
         CameraSelector cameraSelector = new CameraSelector.Builder()
                 .requireLensFacing(lensFacing).build();
         CameraUseCaseAdapter cameraUseCaseAdapter =
-                CameraUtil.getCameraUseCaseAdapter(mContext, cameraSelector);
+                CameraUtil.createCameraUseCaseAdapter(mContext, cameraSelector);
         mInstrumentation.runOnMainSync(() -> {
             try {
                 cameraUseCaseAdapter.addUseCases(Collections.singleton(useCase));
@@ -186,7 +186,7 @@
         CameraSelector cameraSelector = new CameraSelector.Builder()
                 .requireLensFacing(lensFacing).build();
         CameraUseCaseAdapter cameraUseCaseAdapter =
-                CameraUtil.getCameraUseCaseAdapter(mContext, cameraSelector);
+                CameraUtil.createCameraUseCaseAdapter(mContext, cameraSelector);
         mInstrumentation.runOnMainSync(() -> {
             try {
                 cameraUseCaseAdapter.addUseCases(Collections.singleton(useCase));
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 2de4432..062dd05 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
@@ -150,7 +150,7 @@
         Preview useCase = configBuilder.build();
 
         CameraUseCaseAdapter cameraUseCaseAdapter =
-                CameraUtil.getCameraUseCaseAdapter(mContext, cameraSelector);
+                CameraUtil.createCameraUseCaseAdapter(mContext, cameraSelector);
         mInstrumentation.runOnMainSync(() -> {
             // To set the update listener and Preview will change to active state.
             useCase.setSurfaceProvider(
@@ -228,7 +228,7 @@
 
         Preview preview = configBuilder.build();
         CameraUseCaseAdapter cameraUseCaseAdapter =
-                CameraUtil.getCameraUseCaseAdapter(mContext, cameraSelector);
+                CameraUtil.createCameraUseCaseAdapter(mContext, cameraSelector);
         mInstrumentation.runOnMainSync(() -> {
             // To set the update listener and Preview will change to active state.
             preview.setSurfaceProvider(
@@ -284,7 +284,7 @@
         Preview preview = configBuilder.build();
 
         CameraUseCaseAdapter cameraUseCaseAdapter =
-                CameraUtil.getCameraUseCaseAdapter(mContext, cameraSelector);
+                CameraUtil.createCameraUseCaseAdapter(mContext, cameraSelector);
         mInstrumentation.runOnMainSync(() -> {
             // To set the update listener and Preview will change to active state.
             preview.setSurfaceProvider(
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
index 4b9ee8d..a6e2ba6 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
@@ -39,6 +39,7 @@
 import androidx.camera.extensions.BeautyPreviewExtender;
 import androidx.camera.extensions.BokehImageCaptureExtender;
 import androidx.camera.extensions.BokehPreviewExtender;
+import androidx.camera.extensions.Extensions;
 import androidx.camera.extensions.ExtensionsManager;
 import androidx.camera.extensions.ExtensionsManager.EffectMode;
 import androidx.camera.extensions.ExtensionsManager.ExtensionsAvailability;
@@ -87,6 +88,22 @@
         });
     }
 
+    @NonNull
+    public static Collection<Object[]> getAllExtensionsLensFacingCombinations() {
+        return Arrays.asList(new Object[][]{
+                {Extensions.EXTENSION_MODE_BOKEH, CameraSelector.LENS_FACING_FRONT},
+                {Extensions.EXTENSION_MODE_BOKEH, CameraSelector.LENS_FACING_BACK},
+                {Extensions.EXTENSION_MODE_HDR, CameraSelector.LENS_FACING_FRONT},
+                {Extensions.EXTENSION_MODE_HDR, CameraSelector.LENS_FACING_BACK},
+                {Extensions.EXTENSION_MODE_BEAUTY, CameraSelector.LENS_FACING_FRONT},
+                {Extensions.EXTENSION_MODE_BEAUTY, CameraSelector.LENS_FACING_BACK},
+                {Extensions.EXTENSION_MODE_NIGHT, CameraSelector.LENS_FACING_FRONT},
+                {Extensions.EXTENSION_MODE_NIGHT, CameraSelector.LENS_FACING_BACK},
+                {Extensions.EXTENSION_MODE_AUTO, CameraSelector.LENS_FACING_FRONT},
+                {Extensions.EXTENSION_MODE_AUTO, CameraSelector.LENS_FACING_BACK}
+        });
+    }
+
     /**
      * Initializes the extensions for running the following tests.
      *
@@ -108,6 +125,43 @@
         return true;
     }
 
+    @Extensions.ExtensionMode
+    public static int effectModeToExtensionMode(@NonNull EffectMode effectMode) {
+        switch (effectMode) {
+            case NORMAL:
+                return Extensions.EXTENSION_MODE_NONE;
+            case BOKEH:
+                return Extensions.EXTENSION_MODE_BOKEH;
+            case HDR:
+                return Extensions.EXTENSION_MODE_HDR;
+            case NIGHT:
+                return Extensions.EXTENSION_MODE_NIGHT;
+            case BEAUTY:
+                return Extensions.EXTENSION_MODE_BEAUTY;
+            case AUTO:
+                return Extensions.EXTENSION_MODE_AUTO;
+        }
+        throw new IllegalArgumentException("Effect mode not found: " + effectMode);
+    }
+
+    public static EffectMode extensionModeToEffectMode(@Extensions.ExtensionMode int mode) {
+        switch (mode) {
+            case Extensions.EXTENSION_MODE_NONE:
+                return EffectMode.NORMAL;
+            case Extensions.EXTENSION_MODE_BOKEH:
+                return EffectMode.BOKEH;
+            case Extensions.EXTENSION_MODE_HDR:
+                return EffectMode.HDR;
+            case Extensions.EXTENSION_MODE_NIGHT:
+                return EffectMode.NIGHT;
+            case Extensions.EXTENSION_MODE_BEAUTY:
+                return EffectMode.BEAUTY;
+            case Extensions.EXTENSION_MODE_AUTO:
+                return EffectMode.AUTO;
+        }
+        throw new IllegalArgumentException("Extension mode not found: " + mode);
+    }
+
     /**
      * Creates an {@link ImageCapture.Builder} object for specific {@link EffectMode} and
      * {@link CameraSelector.LensFacing}.
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionCameraFilter.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionCameraFilter.java
index 2b30d8c..8a71864 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionCameraFilter.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionCameraFilter.java
@@ -19,8 +19,11 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.experimental.UseExperimental;
+import androidx.camera.camera2.interop.Camera2CameraInfo;
+import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.Camera;
 import androidx.camera.core.CameraFilter;
+import androidx.camera.core.CameraInfo;
 import androidx.camera.core.ExperimentalCameraFilter;
 import androidx.camera.core.impl.CameraInternal;
 import androidx.camera.extensions.impl.ImageCaptureExtenderImpl;
@@ -35,8 +38,8 @@
  */
 @UseExperimental(markerClass = ExperimentalCameraFilter.class)
 public final class ExtensionCameraFilter implements CameraFilter {
-    private PreviewExtenderImpl mPreviewExtenderImpl;
-    private ImageCaptureExtenderImpl mImageCaptureExtenderImpl;
+    private final PreviewExtenderImpl mPreviewExtenderImpl;
+    private final ImageCaptureExtenderImpl mImageCaptureExtenderImpl;
 
     ExtensionCameraFilter(@Nullable PreviewExtenderImpl previewExtenderImpl) {
         mPreviewExtenderImpl = previewExtenderImpl;
@@ -54,6 +57,7 @@
         mImageCaptureExtenderImpl = imageCaptureExtenderImpl;
     }
 
+    @UseExperimental(markerClass = ExperimentalCamera2Interop.class)
     @NonNull
     @Override
     public LinkedHashSet<Camera> filter(@NonNull LinkedHashSet<Camera> cameras) {
@@ -62,6 +66,7 @@
             Preconditions.checkState(camera instanceof CameraInternal,
                     "The camera doesn't contain internal implementation.");
             String cameraId = ((CameraInternal) camera).getCameraInfoInternal().getCameraId();
+            CameraInfo cameraInfo = camera.getCameraInfo();
 
             boolean available = true;
 
@@ -69,12 +74,12 @@
             if (mPreviewExtenderImpl != null) {
                 available =
                         mPreviewExtenderImpl.isExtensionAvailable(cameraId,
-                                CameraUtil.getCameraCharacteristics(cameraId));
+                                Camera2CameraInfo.extractCameraCharacteristics(cameraInfo));
             }
             // 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));
+                        Camera2CameraInfo.extractCameraCharacteristics(cameraInfo));
             }
 
             if (available) {
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/Extensions.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/Extensions.java
index 7d1e65b..42cd607 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/Extensions.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/Extensions.java
@@ -21,11 +21,26 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
+import androidx.annotation.experimental.UseExperimental;
 import androidx.camera.core.Camera;
+import androidx.camera.core.CameraFilter;
+import androidx.camera.core.ExperimentalCameraFilter;
 import androidx.camera.core.UseCase;
+import androidx.camera.core.impl.CameraFilters;
+import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl;
+import androidx.camera.extensions.impl.AutoPreviewExtenderImpl;
+import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl;
+import androidx.camera.extensions.impl.BeautyPreviewExtenderImpl;
+import androidx.camera.extensions.impl.BokehImageCaptureExtenderImpl;
+import androidx.camera.extensions.impl.BokehPreviewExtenderImpl;
+import androidx.camera.extensions.impl.HdrImageCaptureExtenderImpl;
+import androidx.camera.extensions.impl.HdrPreviewExtenderImpl;
+import androidx.camera.extensions.impl.NightImageCaptureExtenderImpl;
+import androidx.camera.extensions.impl.NightPreviewExtenderImpl;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.LinkedHashSet;
 import java.util.List;
 
 /**
@@ -127,10 +142,51 @@
      * #checkUseCases(Camera, List, int)}.
      *
      * @param camera The Camera to check if it supports the extension.
-     * @param mode The extension mode to check
+     * @param mode   The extension mode to check
      */
+    @UseExperimental(markerClass = ExperimentalCameraFilter.class)
     public boolean isExtensionAvailable(@NonNull Camera camera, @ExtensionMode int mode) {
-        throw new UnsupportedOperationException("not yet implemented");
+        CameraFilter filter = getFilter(mode);
+
+        // Extension is available for the camera if it contains a CameraInternal which supports
+        // the extension
+        return !filter.filter(new LinkedHashSet<>(camera.getCameraInternals())).isEmpty();
+    }
+
+    @UseExperimental(markerClass = ExperimentalCameraFilter.class)
+    private CameraFilter getFilter(@ExtensionMode int mode) {
+        CameraFilter filter;
+        try {
+            switch (mode) {
+                case EXTENSION_MODE_BOKEH:
+                    filter = new ExtensionCameraFilter(new BokehPreviewExtenderImpl(),
+                            new BokehImageCaptureExtenderImpl());
+                    break;
+                case EXTENSION_MODE_HDR:
+                    filter = new ExtensionCameraFilter(new HdrPreviewExtenderImpl(),
+                            new HdrImageCaptureExtenderImpl());
+                    break;
+                case EXTENSION_MODE_NIGHT:
+                    filter = new ExtensionCameraFilter(new NightPreviewExtenderImpl(),
+                            new NightImageCaptureExtenderImpl());
+                    break;
+                case EXTENSION_MODE_BEAUTY:
+                    filter = new ExtensionCameraFilter(new BeautyPreviewExtenderImpl(),
+                            new BeautyImageCaptureExtenderImpl());
+                    break;
+                case EXTENSION_MODE_AUTO:
+                    filter = new ExtensionCameraFilter(new AutoPreviewExtenderImpl(),
+                            new AutoImageCaptureExtenderImpl());
+                    break;
+                case EXTENSION_MODE_NONE:
+                default:
+                    filter = CameraFilters.ANY;
+            }
+        } catch (NoClassDefFoundError e) {
+            filter = CameraFilters.NONE;
+        }
+
+        return filter;
     }
 
     /**
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java
index 1c1fb28..a5b7bed 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java
@@ -22,6 +22,7 @@
 import androidx.camera.core.CameraControl;
 import androidx.camera.core.CameraInfo;
 import androidx.camera.core.UseCase;
+import androidx.camera.core.impl.CameraInternal;
 import androidx.camera.core.internal.CameraUseCaseAdapter;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.Lifecycle.State;
@@ -223,15 +224,24 @@
         }
     }
 
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    // Camera interface
+    ////////////////////////////////////////////////////////////////////////////////////////////////
     @NonNull
     @Override
     public CameraControl getCameraControl() {
-        return mCameraUseCaseAdapter.getCameraControlInternal();
+        return mCameraUseCaseAdapter.getCameraControl();
     }
 
     @NonNull
     @Override
     public CameraInfo getCameraInfo() {
-        return mCameraUseCaseAdapter.getCameraInfoInternal();
+        return mCameraUseCaseAdapter.getCameraInfo();
+    }
+
+    @NonNull
+    @Override
+    public Collection<CameraInternal> getCameraInternals() {
+        return mCameraUseCaseAdapter.getCameraInternals();
     }
 }
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 bb93ca4..3c9294a 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
@@ -260,17 +260,23 @@
 
 
     /**
-     * Retrieves the CameraUseCaseAdapter that would be created with the given CameraSelector.
+     * Creates the CameraUseCaseAdapter that would be created with the given CameraSelector.
      *
      * <p> This requires that {@link CameraX#initialize(Context, CameraXConfig)} has been called
      * to properly initialize the cameras.
      *
+     * <p>A new CameraUseCaseAdapter instance will be created every time this method is called.
+     * UseCases previously attached to CameraUseCasesAdapters returned by this method or
+     * {@link #createCameraAndAttachUseCase(Context, CameraSelector, UseCase...)} will not be
+     * attached to the new CameraUseCaseAdapter returned by this method.
+     *
      * @param context        The context used to initialize CameraX
      * @param cameraSelector The selector to select cameras with.
      * @hide
      */
     @RestrictTo(RestrictTo.Scope.TESTS)
-    public static CameraUseCaseAdapter getCameraUseCaseAdapter(@NonNull Context context,
+    @NonNull
+    public static CameraUseCaseAdapter createCameraUseCaseAdapter(@NonNull Context context,
             @NonNull CameraSelector cameraSelector) {
         try {
             CameraX cameraX = CameraX.getOrCreateInstance(context).get(5000, TimeUnit.MILLISECONDS);
@@ -284,21 +290,27 @@
     }
 
     /**
-     * Retrieves the CameraUseCaseAdapter that would be created with the given CameraSelector and
+     * Creates the CameraUseCaseAdapter that would be created with the given CameraSelector and
      * attaches the UseCases.
      *
      * <p> This requires that {@link CameraX#initialize(Context, CameraXConfig)} has been called
      * to properly initialize the cameras.
      *
+     * <p>A new CameraUseCaseAdapter instance will be created every time this method is called.
+     * UseCases previously attached to CameraUseCasesAdapters returned by this method or
+     * {@link #createCameraUseCaseAdapter(Context, CameraSelector)} will not be
+     * attached to the new CameraUseCaseAdapter returned by this method.
+     *
      * @param context        The context used to initialize CameraX
      * @param cameraSelector The selector to select cameras with.
      * @param useCases       The UseCases to attach to the CameraUseCaseAdapter.
      * @hide
      */
     @RestrictTo(RestrictTo.Scope.TESTS)
-    public static CameraUseCaseAdapter getCameraAndAttachUseCase(@NonNull Context context,
+    @NonNull
+    public static CameraUseCaseAdapter createCameraAndAttachUseCase(@NonNull Context context,
             @NonNull CameraSelector cameraSelector, @NonNull UseCase... useCases) {
-        CameraUseCaseAdapter cameraUseCaseAdapter = getCameraUseCaseAdapter(context,
+        CameraUseCaseAdapter cameraUseCaseAdapter = createCameraUseCaseAdapter(context,
                 cameraSelector);
 
         // TODO(b/160249108) move off of main thread once UseCases can be attached on any
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java
index 250af83..3a6ef21 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java
@@ -22,8 +22,6 @@
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.camera.core.CameraControl;
-import androidx.camera.core.CameraInfo;
 import androidx.camera.core.Logger;
 import androidx.camera.core.UseCase;
 import androidx.camera.core.impl.CameraControlInternal;
@@ -378,17 +376,4 @@
         // notifySurfaceDetached calls.
         mConfiguredDeferrableSurfaces.clear();
     }
-
-    @NonNull
-    @Override
-    public CameraControl getCameraControl() {
-        return getCameraControlInternal();
-    }
-
-    @NonNull
-    @Override
-    public CameraInfo getCameraInfo() {
-        return getCameraInfoInternal();
-    }
-
 }
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java b/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java
index 0ec749a..7d99923 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java
@@ -145,7 +145,7 @@
 
     private final Preview.SurfaceProvider mSurfaceProvider = surfaceRequest -> {
         Logger.d(TAG, "Surface requested by Preview.");
-        CameraInternal camera = (CameraInternal) surfaceRequest.getCamera();
+        CameraInternal camera = surfaceRequest.getCamera();
         mPreviewTransform.setSensorDimensionFlipNeeded(
                 isSensorDimensionFlipNeeded(camera.getCameraInfo()));
         mImplementation = surfaceRequest.isRGBA8888Required() || shouldUseTextureView(
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.java b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.java
index 48f35b2..0aeb641 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.java
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/PreviewProcessorTimestampTest.java
@@ -261,7 +261,7 @@
                     }));
 
             CameraUseCaseAdapter cameraUseCaseAdapter =
-                    CameraUtil.getCameraUseCaseAdapter(mContext, cameraSelector);
+                    CameraUtil.createCameraUseCaseAdapter(mContext, cameraSelector);
             try {
                 cameraUseCaseAdapter.addUseCases(Arrays.asList(preview, imageCapture));
             } catch (CameraUseCaseAdapter.CameraException e) {
diff --git a/camera/integration-tests/uiwidgetstestapp/build.gradle b/camera/integration-tests/uiwidgetstestapp/build.gradle
index 7c32484..c6d1b24 100644
--- a/camera/integration-tests/uiwidgetstestapp/build.gradle
+++ b/camera/integration-tests/uiwidgetstestapp/build.gradle
@@ -59,7 +59,7 @@
     implementation(project(":camera:camera-view"))
 
     // Android Support Library
-    implementation("androidx.appcompat:appcompat:1.1.0")
+    implementation("androidx.appcompat:appcompat:1.2.0")
     implementation("androidx.viewpager2:viewpager2:1.0.0")
 
     // Testing framework
@@ -73,5 +73,5 @@
     androidTestImplementation(project(':internal-testutils-runtime'))
     androidTestImplementation(TRUTH)
     debugImplementation(ANDROIDX_TEST_CORE)
-    debugImplementation("androidx.fragment:fragment-testing:1.2.3")
+    debugImplementation("androidx.fragment:fragment-testing:1.2.5")
 }
\ No newline at end of file
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureBaseTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureBaseTest.kt
index f0709a4..c06425b 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureBaseTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/rotations/ImageCaptureBaseTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.Intent
 import android.os.Build
+import android.os.Environment
 import android.view.View
 import androidx.camera.core.CameraSelector
 import androidx.camera.integration.uiwidgets.R
@@ -29,8 +30,8 @@
 import androidx.test.uiautomator.UiDevice
 import androidx.testutils.withActivity
 import com.google.common.truth.Truth.assertThat
-import org.junit.Assume
 import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
 import java.util.concurrent.TimeUnit
 
 /**
@@ -59,17 +60,29 @@
         )
 
         CoreAppTestUtil.assumeCompatibleDevice()
-        Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(lensFacing))
+        assumeTrue(CameraUtil.hasCameraWithLensFacing(lensFacing))
 
         // Clear the device's UI and ensure it's in a natural orientation
         CoreAppTestUtil.clearDeviceUI(InstrumentationRegistry.getInstrumentation())
         mDevice.setOrientationNatural()
+
+        // Create pictures folder if it doesn't exist on the device. If this fails, abort test.
+        assumeTrue("Failed to create pictures directory", createPicturesFolder())
     }
 
     protected fun tearDown() {
         mDevice.unfreezeRotation()
     }
 
+    @Suppress("DEPRECATION")
+    private fun createPicturesFolder(): Boolean {
+        val folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
+        if (folder.exists()) {
+            return true
+        }
+        return folder.mkdir()
+    }
+
     protected inline fun <reified A : CameraActivity> verifyRotation(
         lensFacing: Int,
         captureMode: Int,
diff --git a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/rotations/CameraActivity.kt b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/rotations/CameraActivity.kt
index 50e8ebd..7cd8d9a 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/rotations/CameraActivity.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/main/java/androidx/camera/integration/uiwidgets/rotations/CameraActivity.kt
@@ -36,6 +36,7 @@
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
 import androidx.camera.integration.uiwidgets.R
 import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.core.app.ActivityCompat
 import androidx.core.content.ContextCompat
 import kotlinx.android.synthetic.main.activity_rotations_main.previewView
 import java.io.File
@@ -56,7 +57,7 @@
         setContentView(R.layout.activity_rotations_main)
         mAnalysisExecutor = Executors.newSingleThreadExecutor()
         if (shouldRequestPermissionsAtRuntime() && !hasPermissions()) {
-            requestPermissions(PERMISSIONS, REQUEST_CODE_PERMISSIONS)
+            ActivityCompat.requestPermissions(this, PERMISSIONS, REQUEST_CODE_PERMISSIONS)
         } else {
             setUpCamera()
         }
@@ -163,12 +164,12 @@
                     mCaptureResult = ImageCaptureResult.InMemory(image)
                     mCaptureDone.release()
                     image.close()
-                    Log.d(TAG, "MediaStore captured successful")
+                    Log.d(TAG, "InMemory image capture successful")
                 }
 
                 override fun onError(exception: ImageCaptureException) {
                     mCaptureDone.release()
-                    Log.e(TAG, "InMemory capture failed", exception)
+                    Log.e(TAG, "InMemory image capture failed", exception)
                 }
             })
     }
@@ -183,12 +184,12 @@
                 override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                     mCaptureResult = ImageCaptureResult.FileOrOutputStream(imageFile)
                     mCaptureDone.release()
-                    Log.d(TAG, "MediaStore captured successful")
+                    Log.d(TAG, "File image capture successful")
                 }
 
                 override fun onError(exception: ImageCaptureException) {
                     mCaptureDone.release()
-                    Log.e(TAG, "File capture failed", exception)
+                    Log.e(TAG, "File image capture failed", exception)
                 }
             })
     }
@@ -204,12 +205,12 @@
                 override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                     mCaptureResult = ImageCaptureResult.FileOrOutputStream(imageFile)
                     mCaptureDone.release()
-                    Log.d(TAG, "MediaStore captured successful")
+                    Log.d(TAG, "OutputStream image capture successful")
                 }
 
                 override fun onError(exception: ImageCaptureException) {
                     mCaptureDone.release()
-                    Log.e(TAG, "OutputStream capture failed", exception)
+                    Log.e(TAG, "OutputStream image capture failed", exception)
                 }
             })
     }
@@ -230,12 +231,12 @@
                     mCaptureResult =
                         ImageCaptureResult.MediaStore(contentResolver, outputFileResults.savedUri!!)
                     mCaptureDone.release()
-                    Log.d(TAG, "MediaStore captured successful")
+                    Log.d(TAG, "MediaStore image capture successful")
                 }
 
                 override fun onError(exception: ImageCaptureException) {
                     mCaptureDone.release()
-                    Log.e(TAG, "MediaStore capture failed", exception)
+                    Log.e(TAG, "MediaStore image capture failed", exception)
                 }
             })
     }
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index a5be571..06bf3318 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -86,7 +86,11 @@
   }
 
   public final class ButtonConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.animation.core.AnimatedValue<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> defaultAnimatedElevation-hoVAPrg(boolean enabled, androidx.compose.foundation.InteractionState interactionState, float defaultElevation = 2.dp, float pressedElevation = 8.dp, float disabledElevation = 0.dp);
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.core.AnimatedValue<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> defaultAnimatedElevation-qDazgog(androidx.compose.foundation.InteractionState interactionState, boolean enabled, float defaultElevation = 2.dp, float pressedElevation = 8.dp, float disabledElevation = 0.dp);
+    method @androidx.compose.runtime.Composable public long defaultButtonBackgroundColor-Q31_wr0(boolean enabled, long defaultColor = MaterialTheme.colors.primary, long disabledColor = defaultDisabledBackgroundColor);
+    method @androidx.compose.runtime.Composable public long defaultButtonContentColor-Q31_wr0(boolean enabled, long defaultColor, long disabledColor = defaultDisabledContentColor);
+    method @androidx.compose.runtime.Composable public long defaultOutlinedButtonContentColor-Q31_wr0(boolean enabled, long defaultColor = MaterialTheme.colors.primary, long disabledColor = defaultDisabledContentColor);
+    method @androidx.compose.runtime.Composable public long defaultTextButtonContentColor-Q31_wr0(boolean enabled, long defaultColor = MaterialTheme.colors.primary, long disabledColor = defaultDisabledContentColor);
     method public androidx.compose.foundation.layout.PaddingValues getDefaultContentPadding();
     method public long getDefaultDisabledBackgroundColor();
     method public long getDefaultDisabledContentColor();
@@ -112,15 +116,15 @@
   }
 
   public final class ButtonKt {
-    method @androidx.compose.runtime.Composable public static void Button-Vgmpx9w(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
+    method @androidx.compose.runtime.Composable public static void Button-M91Patw(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
     return <init>()
-}), float elevation = ButtonConstants.defaultAnimatedElevation(enabled, interactionState).value, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = null, long backgroundColor = MaterialTheme.colors.primary, long disabledBackgroundColor = ButtonConstants.defaultDisabledBackgroundColor, long contentColor = contentColorFor(backgroundColor), long disabledContentColor = ButtonConstants.defaultDisabledContentColor, androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static inline void OutlinedButton-UavO1Bo(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
+}), float elevation = ButtonConstants.defaultAnimatedElevation(interactionState, enabled).value, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = null, long backgroundColor = ButtonConstants.defaultButtonBackgroundColor(enabled), long contentColor = ButtonConstants.defaultButtonContentColor(enabled, contentColorFor(backgroundColor)), androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void OutlinedButton-M91Patw(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
     return <init>()
-}), float elevation = 0.dp, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = ButtonConstants.defaultOutlinedBorder, long backgroundColor = MaterialTheme.colors.surface, long contentColor = MaterialTheme.colors.primary, long disabledContentColor = ButtonConstants.defaultDisabledContentColor, androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static inline void TextButton-UavO1Bo(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
+}), float elevation = 0.dp, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = ButtonConstants.defaultOutlinedBorder, long backgroundColor = MaterialTheme.colors.surface, long contentColor = ButtonConstants.defaultOutlinedButtonContentColor(enabled), androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void TextButton-M91Patw(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
     return <init>()
-}), float elevation = 0.dp, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = null, long backgroundColor = Color.Transparent, long contentColor = MaterialTheme.colors.primary, long disabledContentColor = ButtonConstants.defaultDisabledContentColor, androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultTextContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+}), float elevation = 0.dp, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = null, long backgroundColor = Color.Transparent, long contentColor = ButtonConstants.defaultTextButtonContentColor(enabled), androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultTextContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
   }
 
   public final class CardKt {
@@ -567,8 +571,9 @@
   }
 
   public final class TextFieldKt {
-    method @androidx.compose.runtime.Composable public static void TextField-enE39lU(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  _, _ ->  }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error, long backgroundColor = MaterialTheme.colors.onSurface, androidx.compose.ui.graphics.Shape shape = MaterialTheme.shapes.small.copy(ZeroCornerSize, ZeroCornerSize));
-    method @androidx.compose.runtime.Composable public static void TextField-tHV9mug(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  _, _ ->  }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error, long backgroundColor = MaterialTheme.colors.onSurface, androidx.compose.ui.graphics.Shape shape = MaterialTheme.shapes.small.copy(ZeroCornerSize, ZeroCornerSize));
+    method @androidx.compose.runtime.Composable public static void TextField-enE39lU(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  _, _ ->  }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error, long backgroundColor = MaterialTheme.colors.onSurface.copy(ContainerAlpha), androidx.compose.ui.graphics.Shape shape = MaterialTheme.shapes.small.copy(ZeroCornerSize, ZeroCornerSize));
+    method @androidx.compose.runtime.Composable public static void TextField-tHV9mug(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  _, _ ->  }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error, long backgroundColor = MaterialTheme.colors.onSurface.copy(ContainerAlpha), androidx.compose.ui.graphics.Shape shape = MaterialTheme.shapes.small.copy(ZeroCornerSize, ZeroCornerSize));
+    field public static final float ContainerAlpha = 0.12f;
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ThresholdConfig {
diff --git a/compose/material/material/api/public_plus_experimental_current.txt b/compose/material/material/api/public_plus_experimental_current.txt
index a5be571..06bf3318 100644
--- a/compose/material/material/api/public_plus_experimental_current.txt
+++ b/compose/material/material/api/public_plus_experimental_current.txt
@@ -86,7 +86,11 @@
   }
 
   public final class ButtonConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.animation.core.AnimatedValue<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> defaultAnimatedElevation-hoVAPrg(boolean enabled, androidx.compose.foundation.InteractionState interactionState, float defaultElevation = 2.dp, float pressedElevation = 8.dp, float disabledElevation = 0.dp);
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.core.AnimatedValue<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> defaultAnimatedElevation-qDazgog(androidx.compose.foundation.InteractionState interactionState, boolean enabled, float defaultElevation = 2.dp, float pressedElevation = 8.dp, float disabledElevation = 0.dp);
+    method @androidx.compose.runtime.Composable public long defaultButtonBackgroundColor-Q31_wr0(boolean enabled, long defaultColor = MaterialTheme.colors.primary, long disabledColor = defaultDisabledBackgroundColor);
+    method @androidx.compose.runtime.Composable public long defaultButtonContentColor-Q31_wr0(boolean enabled, long defaultColor, long disabledColor = defaultDisabledContentColor);
+    method @androidx.compose.runtime.Composable public long defaultOutlinedButtonContentColor-Q31_wr0(boolean enabled, long defaultColor = MaterialTheme.colors.primary, long disabledColor = defaultDisabledContentColor);
+    method @androidx.compose.runtime.Composable public long defaultTextButtonContentColor-Q31_wr0(boolean enabled, long defaultColor = MaterialTheme.colors.primary, long disabledColor = defaultDisabledContentColor);
     method public androidx.compose.foundation.layout.PaddingValues getDefaultContentPadding();
     method public long getDefaultDisabledBackgroundColor();
     method public long getDefaultDisabledContentColor();
@@ -112,15 +116,15 @@
   }
 
   public final class ButtonKt {
-    method @androidx.compose.runtime.Composable public static void Button-Vgmpx9w(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
+    method @androidx.compose.runtime.Composable public static void Button-M91Patw(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
     return <init>()
-}), float elevation = ButtonConstants.defaultAnimatedElevation(enabled, interactionState).value, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = null, long backgroundColor = MaterialTheme.colors.primary, long disabledBackgroundColor = ButtonConstants.defaultDisabledBackgroundColor, long contentColor = contentColorFor(backgroundColor), long disabledContentColor = ButtonConstants.defaultDisabledContentColor, androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static inline void OutlinedButton-UavO1Bo(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
+}), float elevation = ButtonConstants.defaultAnimatedElevation(interactionState, enabled).value, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = null, long backgroundColor = ButtonConstants.defaultButtonBackgroundColor(enabled), long contentColor = ButtonConstants.defaultButtonContentColor(enabled, contentColorFor(backgroundColor)), androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void OutlinedButton-M91Patw(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
     return <init>()
-}), float elevation = 0.dp, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = ButtonConstants.defaultOutlinedBorder, long backgroundColor = MaterialTheme.colors.surface, long contentColor = MaterialTheme.colors.primary, long disabledContentColor = ButtonConstants.defaultDisabledContentColor, androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static inline void TextButton-UavO1Bo(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
+}), float elevation = 0.dp, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = ButtonConstants.defaultOutlinedBorder, long backgroundColor = MaterialTheme.colors.surface, long contentColor = ButtonConstants.defaultOutlinedButtonContentColor(enabled), androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void TextButton-M91Patw(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
     return <init>()
-}), float elevation = 0.dp, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = null, long backgroundColor = Color.Transparent, long contentColor = MaterialTheme.colors.primary, long disabledContentColor = ButtonConstants.defaultDisabledContentColor, androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultTextContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+}), float elevation = 0.dp, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = null, long backgroundColor = Color.Transparent, long contentColor = ButtonConstants.defaultTextButtonContentColor(enabled), androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultTextContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
   }
 
   public final class CardKt {
@@ -567,8 +571,9 @@
   }
 
   public final class TextFieldKt {
-    method @androidx.compose.runtime.Composable public static void TextField-enE39lU(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  _, _ ->  }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error, long backgroundColor = MaterialTheme.colors.onSurface, androidx.compose.ui.graphics.Shape shape = MaterialTheme.shapes.small.copy(ZeroCornerSize, ZeroCornerSize));
-    method @androidx.compose.runtime.Composable public static void TextField-tHV9mug(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  _, _ ->  }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error, long backgroundColor = MaterialTheme.colors.onSurface, androidx.compose.ui.graphics.Shape shape = MaterialTheme.shapes.small.copy(ZeroCornerSize, ZeroCornerSize));
+    method @androidx.compose.runtime.Composable public static void TextField-enE39lU(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  _, _ ->  }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error, long backgroundColor = MaterialTheme.colors.onSurface.copy(ContainerAlpha), androidx.compose.ui.graphics.Shape shape = MaterialTheme.shapes.small.copy(ZeroCornerSize, ZeroCornerSize));
+    method @androidx.compose.runtime.Composable public static void TextField-tHV9mug(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  _, _ ->  }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error, long backgroundColor = MaterialTheme.colors.onSurface.copy(ContainerAlpha), androidx.compose.ui.graphics.Shape shape = MaterialTheme.shapes.small.copy(ZeroCornerSize, ZeroCornerSize));
+    field public static final float ContainerAlpha = 0.12f;
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ThresholdConfig {
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index a5be571..06bf3318 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -86,7 +86,11 @@
   }
 
   public final class ButtonConstants {
-    method @androidx.compose.runtime.Composable public androidx.compose.animation.core.AnimatedValue<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> defaultAnimatedElevation-hoVAPrg(boolean enabled, androidx.compose.foundation.InteractionState interactionState, float defaultElevation = 2.dp, float pressedElevation = 8.dp, float disabledElevation = 0.dp);
+    method @androidx.compose.runtime.Composable public androidx.compose.animation.core.AnimatedValue<androidx.compose.ui.unit.Dp,androidx.compose.animation.core.AnimationVector1D> defaultAnimatedElevation-qDazgog(androidx.compose.foundation.InteractionState interactionState, boolean enabled, float defaultElevation = 2.dp, float pressedElevation = 8.dp, float disabledElevation = 0.dp);
+    method @androidx.compose.runtime.Composable public long defaultButtonBackgroundColor-Q31_wr0(boolean enabled, long defaultColor = MaterialTheme.colors.primary, long disabledColor = defaultDisabledBackgroundColor);
+    method @androidx.compose.runtime.Composable public long defaultButtonContentColor-Q31_wr0(boolean enabled, long defaultColor, long disabledColor = defaultDisabledContentColor);
+    method @androidx.compose.runtime.Composable public long defaultOutlinedButtonContentColor-Q31_wr0(boolean enabled, long defaultColor = MaterialTheme.colors.primary, long disabledColor = defaultDisabledContentColor);
+    method @androidx.compose.runtime.Composable public long defaultTextButtonContentColor-Q31_wr0(boolean enabled, long defaultColor = MaterialTheme.colors.primary, long disabledColor = defaultDisabledContentColor);
     method public androidx.compose.foundation.layout.PaddingValues getDefaultContentPadding();
     method public long getDefaultDisabledBackgroundColor();
     method public long getDefaultDisabledContentColor();
@@ -112,15 +116,15 @@
   }
 
   public final class ButtonKt {
-    method @androidx.compose.runtime.Composable public static void Button-Vgmpx9w(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
+    method @androidx.compose.runtime.Composable public static void Button-M91Patw(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
     return <init>()
-}), float elevation = ButtonConstants.defaultAnimatedElevation(enabled, interactionState).value, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = null, long backgroundColor = MaterialTheme.colors.primary, long disabledBackgroundColor = ButtonConstants.defaultDisabledBackgroundColor, long contentColor = contentColorFor(backgroundColor), long disabledContentColor = ButtonConstants.defaultDisabledContentColor, androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static inline void OutlinedButton-UavO1Bo(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
+}), float elevation = ButtonConstants.defaultAnimatedElevation(interactionState, enabled).value, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = null, long backgroundColor = ButtonConstants.defaultButtonBackgroundColor(enabled), long contentColor = ButtonConstants.defaultButtonContentColor(enabled, contentColorFor(backgroundColor)), androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void OutlinedButton-M91Patw(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
     return <init>()
-}), float elevation = 0.dp, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = ButtonConstants.defaultOutlinedBorder, long backgroundColor = MaterialTheme.colors.surface, long contentColor = MaterialTheme.colors.primary, long disabledContentColor = ButtonConstants.defaultDisabledContentColor, androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static inline void TextButton-UavO1Bo(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
+}), float elevation = 0.dp, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = ButtonConstants.defaultOutlinedBorder, long backgroundColor = MaterialTheme.colors.surface, long contentColor = ButtonConstants.defaultOutlinedButtonContentColor(enabled), androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void TextButton-M91Patw(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, androidx.compose.ui.Modifier modifier = Modifier, boolean enabled = true, androidx.compose.foundation.InteractionState interactionState = remember({ 
     return <init>()
-}), float elevation = 0.dp, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = null, long backgroundColor = Color.Transparent, long contentColor = MaterialTheme.colors.primary, long disabledContentColor = ButtonConstants.defaultDisabledContentColor, androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultTextContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+}), float elevation = 0.dp, androidx.compose.ui.graphics.Shape shape = small, androidx.compose.foundation.BorderStroke? border = null, long backgroundColor = Color.Transparent, long contentColor = ButtonConstants.defaultTextButtonContentColor(enabled), androidx.compose.foundation.layout.PaddingValues contentPadding = ButtonConstants.DefaultTextContentPadding, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
   }
 
   public final class CardKt {
@@ -567,8 +571,9 @@
   }
 
   public final class TextFieldKt {
-    method @androidx.compose.runtime.Composable public static void TextField-enE39lU(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  _, _ ->  }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error, long backgroundColor = MaterialTheme.colors.onSurface, androidx.compose.ui.graphics.Shape shape = MaterialTheme.shapes.small.copy(ZeroCornerSize, ZeroCornerSize));
-    method @androidx.compose.runtime.Composable public static void TextField-tHV9mug(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  _, _ ->  }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error, long backgroundColor = MaterialTheme.colors.onSurface, androidx.compose.ui.graphics.Shape shape = MaterialTheme.shapes.small.copy(ZeroCornerSize, ZeroCornerSize));
+    method @androidx.compose.runtime.Composable public static void TextField-enE39lU(String value, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  _, _ ->  }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error, long backgroundColor = MaterialTheme.colors.onSurface.copy(ContainerAlpha), androidx.compose.ui.graphics.Shape shape = MaterialTheme.shapes.small.copy(ZeroCornerSize, ZeroCornerSize));
+    method @androidx.compose.runtime.Composable public static void TextField-tHV9mug(androidx.compose.ui.text.input.TextFieldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.input.TextFieldValue,kotlin.Unit> onValueChange, androidx.compose.ui.Modifier modifier = Modifier, androidx.compose.ui.text.TextStyle textStyle = currentTextStyle(), kotlin.jvm.functions.Function0<kotlin.Unit>? label = null, kotlin.jvm.functions.Function0<kotlin.Unit>? placeholder = null, kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon = null, kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon = null, boolean isErrorValue = false, androidx.compose.ui.text.input.VisualTransformation visualTransformation = VisualTransformation.None, androidx.compose.ui.text.input.KeyboardType keyboardType = KeyboardType.Text, androidx.compose.ui.text.input.ImeAction imeAction = ImeAction.Unspecified, kotlin.jvm.functions.Function2<? super androidx.compose.ui.text.input.ImeAction,? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  _, _ ->  }, kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.SoftwareKeyboardController,kotlin.Unit>  long activeColor = MaterialTheme.colors.primary, long inactiveColor = MaterialTheme.colors.onSurface, long errorColor = MaterialTheme.colors.error, long backgroundColor = MaterialTheme.colors.onSurface.copy(ContainerAlpha), androidx.compose.ui.graphics.Shape shape = MaterialTheme.shapes.small.copy(ZeroCornerSize, ZeroCornerSize));
+    field public static final float ContainerAlpha = 0.12f;
   }
 
   @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Stable public interface ThresholdConfig {
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ButtonTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ButtonTest.kt
index d62ada4..725776a 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ButtonTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/ButtonTest.kt
@@ -397,7 +397,10 @@
                     Button(
                         >
                         enabled = false,
-                        backgroundColor = Color.Red,
+                        backgroundColor = ButtonConstants.defaultButtonBackgroundColor(
+                            enabled = false,
+                            defaultColor = Color.Red
+                        ),
                         shape = RectangleShape
                     ) {}
                 }
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
index 2201056..b10ec7e 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/textfield/TextFieldTest.kt
@@ -43,7 +43,6 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.node.Ref
 import androidx.compose.ui.onPositioned
@@ -697,7 +696,7 @@
 
     @Test
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-    fun testTextField_alphaNotSet_toBackgroundColorAndTransparentColors() {
+    fun testTextField_alphaNotApplied_toCustomBackgroundColorAndTransparentColors() {
         val latch = CountDownLatch(1)
 
         rule.setMaterialContent {
@@ -717,14 +716,12 @@
             }
         }
 
-        val expectedColor = Color.Blue.copy(alpha = 0.12f).compositeOver(Color.White)
-
         rule.onNodeWithTag(TextfieldTag)
             .captureToBitmap()
             .assertShape(
                 density = rule.density,
                 backgroundColor = Color.White,
-                shapeColor = expectedColor,
+                shapeColor = Color.Blue,
                 shape = RectangleShape,
                 // avoid elevation artifacts
                 shapeOverlapPixelCount = with(rule.density) { 1.dp.toPx() }
@@ -738,7 +735,7 @@
             .assertShape(
                 density = rule.density,
                 backgroundColor = Color.White,
-                shapeColor = expectedColor,
+                shapeColor = Color.Blue,
                 shape = RectangleShape,
                 // avoid elevation artifacts
                 shapeOverlapPixelCount = with(rule.density) { 1.dp.toPx() }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Button.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Button.kt
index 5fa4360..34f0015 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Button.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Button.kt
@@ -84,9 +84,7 @@
  * @param shape Defines the button's shape as well as its shadow
  * @param border Border to draw around the button
  * @param backgroundColor The background color. Use [Color.Transparent] to have no color
- * @param disabledBackgroundColor The background color used when [enabled] is false
  * @param contentColor The preferred content color. Will be used by text and iconography
- * @param disabledContentColor The preferred content color used when [enabled] is false
  * @param contentPadding The spacing values to apply internally between the container and the content
  */
 @Composable
@@ -95,13 +93,14 @@
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     interactionState: InteractionState = remember { InteractionState() },
-    elevation: Dp = ButtonConstants.defaultAnimatedElevation(enabled, interactionState).value,
+    elevation: Dp = ButtonConstants.defaultAnimatedElevation(interactionState, enabled).value,
     shape: Shape = MaterialTheme.shapes.small,
     border: BorderStroke? = null,
-    backgroundColor: Color = MaterialTheme.colors.primary,
-    disabledBackgroundColor: Color = ButtonConstants.defaultDisabledBackgroundColor,
-    contentColor: Color = contentColorFor(backgroundColor),
-    disabledContentColor: Color = ButtonConstants.defaultDisabledContentColor,
+    backgroundColor: Color = ButtonConstants.defaultButtonBackgroundColor(enabled),
+    contentColor: Color = ButtonConstants.defaultButtonContentColor(
+        enabled,
+        contentColorFor(backgroundColor)
+    ),
     contentPadding: PaddingValues = ButtonConstants.DefaultContentPadding,
     content: @Composable RowScope.() -> Unit
 ) {
@@ -111,8 +110,8 @@
     // aosp/1361921)
     Surface(
         shape = shape,
-        color = if (enabled) backgroundColor else disabledBackgroundColor,
-        contentColor = if (enabled) contentColor else disabledContentColor,
+        color = backgroundColor,
+        contentColor = contentColor,
         border = border,
         elevation = elevation,
         modifier = modifier.clickable(
@@ -175,7 +174,6 @@
  * @param border Border to draw around the button
  * @param backgroundColor The background color. Use [Color.Transparent] to have no color
  * @param contentColor The preferred content color. Will be used by text and iconography
- * @param disabledContentColor The preferred content color used when [enabled] is false
  * @param contentPadding The spacing values to apply internally between the container and the content
  */
 @Composable
@@ -188,8 +186,7 @@
     shape: Shape = MaterialTheme.shapes.small,
     border: BorderStroke? = ButtonConstants.defaultOutlinedBorder,
     backgroundColor: Color = MaterialTheme.colors.surface,
-    contentColor: Color = MaterialTheme.colors.primary,
-    disabledContentColor: Color = ButtonConstants.defaultDisabledContentColor,
+    contentColor: Color = ButtonConstants.defaultOutlinedButtonContentColor(enabled),
     contentPadding: PaddingValues = ButtonConstants.DefaultContentPadding,
     noinline content: @Composable RowScope.() -> Unit
 ) = Button(
@@ -201,9 +198,7 @@
     shape = shape,
     border = border,
     backgroundColor = backgroundColor,
-    disabledBackgroundColor = backgroundColor,
     contentColor = contentColor,
-    disabledContentColor = disabledContentColor,
     contentPadding = contentPadding,
     content = content
 )
@@ -240,7 +235,6 @@
  * @param border Border to draw around the button
  * @param backgroundColor The background color. Use [Color.Transparent] to have no color
  * @param contentColor The preferred content color. Will be used by text and iconography
- * @param disabledContentColor The preferred content color used when [enabled] is false
  * @param contentPadding The spacing values to apply internally between the container and the content
  */
 @Composable
@@ -253,8 +247,7 @@
     shape: Shape = MaterialTheme.shapes.small,
     border: BorderStroke? = null,
     backgroundColor: Color = Color.Transparent,
-    contentColor: Color = MaterialTheme.colors.primary,
-    disabledContentColor: Color = ButtonConstants.defaultDisabledContentColor,
+    contentColor: Color = ButtonConstants.defaultTextButtonContentColor(enabled),
     contentPadding: PaddingValues = ButtonConstants.DefaultTextContentPadding,
     noinline content: @Composable RowScope.() -> Unit
 ) = Button(
@@ -266,9 +259,7 @@
     shape = shape,
     border = border,
     backgroundColor = backgroundColor,
-    disabledBackgroundColor = backgroundColor,
     contentColor = contentColor,
-    disabledContentColor = disabledContentColor,
     contentPadding = contentPadding,
     content = content
 )
@@ -292,13 +283,13 @@
 
     /**
      * The default min width applied for the [Button].
-     * Note that you can override it by applying [Modifier.widthIn] directly on [Button].
+     * Note that you can override it by applying Modifier.widthIn directly on [Button].
      */
     val DefaultMinWidth = 64.dp
 
     /**
      * The default min width applied for the [Button].
-     * Note that you can override it by applying [Modifier.heightIn] directly on [Button].
+     * Note that you can override it by applying Modifier.heightIn directly on [Button].
      */
     val DefaultMinHeight = 36.dp
 
@@ -329,10 +320,10 @@
      * Represents the default elevation for a button in different [Interaction]s, and how the
      * elevation animates between them.
      *
-     * @param enabled whether the [Button] is enabled or not. If the [Button] is disabled then
-     * [disabledElevation] will always be used, regardless of the state of [interactionState].
      * @param interactionState the [InteractionState] for this [Button], representing the current
      * visual state, such as whether it is [Interaction.Pressed] or not.
+     * @param enabled whether the [Button] is enabled or not. If the [Button] is disabled then
+     * [disabledElevation] will always be used, regardless of the state of [interactionState].
      * @param defaultElevation the elevation to use when the [Button] is [enabled], and has no
      * other [Interaction]s
      * @param pressedElevation the elevation to use when the [Button] is [enabled] and
@@ -341,8 +332,8 @@
      */
     @Composable
     fun defaultAnimatedElevation(
-        enabled: Boolean,
         interactionState: InteractionState,
+        enabled: Boolean,
         defaultElevation: Dp = 2.dp,
         pressedElevation: Dp = 8.dp,
         // focused: Dp = 4.dp,
@@ -387,7 +378,64 @@
     }
 
     /**
-     * The default disabled background color used by Contained [Button]s
+     * Returns the recommended background color for a [Button] based on its current state.
+     *
+     * @param enabled whether the Button is enabled or not
+     * @param defaultColor the color to use when enabled
+     * @param disabledColor the color to use when disabled
+     */
+    @Composable
+    fun defaultButtonBackgroundColor(
+        enabled: Boolean,
+        defaultColor: Color = MaterialTheme.colors.primary,
+        disabledColor: Color = defaultDisabledBackgroundColor
+    ): Color = if (enabled) defaultColor else disabledColor
+
+    /**
+     * Returns the recommended content color for a [Button] based on its current state.
+     *
+     * @param defaultColor the content color to use when enabled. This should typically be
+     * [contentColorFor] the background color provided to the Button.
+     * @param enabled whether the Button is enabled or not
+     * @param disabledColor the content color to use when disabled
+     */
+    @Composable
+    fun defaultButtonContentColor(
+        enabled: Boolean,
+        defaultColor: Color,
+        disabledColor: Color = defaultDisabledContentColor
+    ): Color = if (enabled) defaultColor else disabledColor
+
+    /**
+     * Returns the recommended content color for an [OutlinedButton] based on its current state.
+     *
+     * @param enabled whether the OutlinedButton is enabled or not
+     * @param defaultColor the content color to use when enabled
+     * @param disabledColor the content color to use when disabled
+     */
+    @Composable
+    fun defaultOutlinedButtonContentColor(
+        enabled: Boolean,
+        defaultColor: Color = MaterialTheme.colors.primary,
+        disabledColor: Color = defaultDisabledContentColor
+    ): Color = if (enabled) defaultColor else disabledColor
+
+    /**
+     * Returns the recommended content color for a [TextButton] based on its current state.
+     *
+     * @param enabled whether the TextButton is enabled or not
+     * @param defaultColor the content color to use when enabled
+     * @param disabledColor the content color to use when disabled
+     */
+    @Composable
+    fun defaultTextButtonContentColor(
+        enabled: Boolean,
+        defaultColor: Color = MaterialTheme.colors.primary,
+        disabledColor: Color = defaultDisabledContentColor
+    ): Color = if (enabled) defaultColor else disabledColor
+
+    /**
+     * The default disabled background color used by [Button]
      */
     @Composable
     val defaultDisabledBackgroundColor
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
index cdb7b94..42c6169 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
@@ -134,8 +134,7 @@
  * focus, and the color of the label and bottom indicator when the text field is not in focus
  * @param errorColor the alternative color of the label, bottom indicator, cursor and trailing icon
  * used when [isErrorValue] is set to true
- * @param backgroundColor the background color of the text field's container. To the color provided
- * here there will be applied a transparency alpha defined by Material Design specifications
+ * @param backgroundColor the background color of the text field's container
  * @param shape the shape of the text field's container
  */
 @Composable
@@ -157,7 +156,7 @@
     activeColor: Color = MaterialTheme.colors.primary,
     inactiveColor: Color = MaterialTheme.colors.onSurface,
     errorColor: Color = MaterialTheme.colors.error,
-    backgroundColor: Color = MaterialTheme.colors.onSurface,
+    backgroundColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContainerAlpha),
     shape: Shape =
         MaterialTheme.shapes.small.copy(bottomLeft = ZeroCornerSize, bottomRight = ZeroCornerSize)
 ) {
@@ -254,8 +253,7 @@
  * focus, and the color of the label and bottom indicator when the text field is not in focus
  * @param errorColor the alternative color of the label, bottom indicator, cursor and trailing icon
  * used when [isErrorValue] is set to true
- * @param backgroundColor the background color of the text field's container. To the color provided
- * here there will be applied a transparency alpha defined by Material Design specifications
+ * @param backgroundColor the background color of the text field's container
  * @param shape the shape of the text field's container
  */
 @Composable
@@ -277,7 +275,7 @@
     activeColor: Color = MaterialTheme.colors.primary,
     inactiveColor: Color = MaterialTheme.colors.onSurface,
     errorColor: Color = MaterialTheme.colors.error,
-    backgroundColor: Color = MaterialTheme.colors.onSurface,
+    backgroundColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContainerAlpha),
     shape: Shape =
         MaterialTheme.shapes.small.copy(bottomLeft = ZeroCornerSize, bottomRight = ZeroCornerSize)
 ) {
@@ -325,7 +323,7 @@
     IconsWithTextFieldLayout(
         modifier = modifier
             .background(
-                color = backgroundColor.applyAlpha(alpha = ContainerAlpha),
+                color = backgroundColor,
                 shape = shape
             )
             .drawIndicatorLine(
@@ -623,4 +621,4 @@
 private val FirstBaselineOffset = 20.dp
 private val LastBaselineOffset = 10.dp
 private val TextFieldTopPadding = 4.dp
-private const val ContainerAlpha = 0.12f
\ No newline at end of file
+const val ContainerAlpha = 0.12f
\ No newline at end of file
diff --git a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveData.kt b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveData.kt
index 3ee2762..ebf86a0 100644
--- a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveData.kt
+++ b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveData.kt
@@ -250,8 +250,8 @@
  * Builds a LiveData that has values yielded from the given [block] that executes on a
  * [LiveDataScope].
  *
- * The [block] starts executing when the returned [LiveData] becomes active ([LiveData.onActive]).
- * If the [LiveData] becomes inactive ([LiveData.onInactive]) while the [block] is executing, it
+ * The [block] starts executing when the returned [LiveData] becomes [active](LiveData.onActive).
+ * If the [LiveData] becomes [inactive](LiveData.onInactive) while the [block] is executing, it
  * will be cancelled after [timeoutInMs] milliseconds unless the [LiveData] becomes active again
  * before that timeout (to gracefully handle cases like Activity rotation). Any value
  * [LiveDataScope.emit]ed from a cancelled [block] will be ignored.
@@ -269,6 +269,12 @@
  * coroutines documentation for details
  * https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html.
  *
+ * The [timeoutInMs] can be changed to fit different use cases better, for example increasing it
+ * will give more time to the [block] to complete even if [LiveData] is inactive. It is good for
+ * cases when [block] is finite (meaning it can complete successfully) and is costly to restart.
+ * Otherwise if a [block] is cheap to restart, decreasing the [timeoutInMs] value will allow to
+ * yield less values that aren't consumed by anything.
+ *
  * ```
  * // a simple LiveData that receives value 3, 3 seconds after being observed for the first time.
  * val data : LiveData<Int> = liveData {
@@ -355,8 +361,8 @@
  * Builds a LiveData that has values yielded from the given [block] that executes on a
  * [LiveDataScope].
  *
- * The [block] starts executing when the returned [LiveData] becomes active ([LiveData.onActive]).
- * If the [LiveData] becomes inactive ([LiveData.onInactive]) while the [block] is executing, it
+ * The [block] starts executing when the returned [LiveData] becomes [active](LiveData.onActive).
+ * If the [LiveData] becomes [inactive](LiveData.onInactive) while the [block] is executing, it
  * will be cancelled after the [timeout] duration unless the [LiveData] becomes active again
  * before that timeout (to gracefully handle cases like Activity rotation). Any value
  * [LiveDataScope.emit]ed from a cancelled [block] will be ignored.
@@ -374,6 +380,12 @@
  * coroutines documentation for details
  * https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html.
  *
+ * The [timeout] can be changed to fit different use cases better, for example increasing it
+ * will give more time to the [block] to complete even if [LiveData] is inactive. It is good for
+ * cases when [block] is finite (meaning it can complete successfully) and is costly to restart.
+ * Otherwise if a [block] is cheap to restart, decreasing the [timeout] value will allow to
+ * yield less values that aren't consumed by anything.
+ *
  * ```
  * // a simple LiveData that receives value 3, 3 seconds after being observed for the first time.
  * val data : LiveData<Int> = liveData {
diff --git a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt
index 2e4e180..c11200d8 100644
--- a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt
+++ b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt
@@ -56,6 +56,11 @@
  * expected to throw, you can use [catch operator][kotlinx.coroutines.flow.catch] on upstream flow
  * to emit a helpful error object.
  *
+ * The [timeoutInMs] can be changed to fit different use cases better, for example increasing it
+ * will give more time to flow to complete before being canceled and is good for finite flows
+ * that are costly to restart. Otherwise if a flow is cheap to restart decreasing the [timeoutInMs]
+ * value will allow to produce less values that aren't consumed by anything.
+ *
  * @param context The CoroutineContext to collect the upstream flow in. Defaults to
  * [EmptyCoroutineContext] combined with
  * [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate]
@@ -125,6 +130,11 @@
  * expected to throw, you can use [catch operator][kotlinx.coroutines.flow.catch] on upstream flow
  * to emit a helpful error object.
  *
+ * The [timeout] can be changed to fit different use cases better, for example increasing it
+ * will give more time to flow to complete before being canceled and is good for finite flows
+ * that are costly to restart. Otherwise if a flow is cheap to restart decreasing the [timeout]
+ * value will allow to produce less values that aren't consumed by anything.
+ *
  * @param context The CoroutineContext to collect the upstream flow in. Defaults to
  * [EmptyCoroutineContext] combined with
  * [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate]
diff --git a/media2/common/api/1.1.0-beta01.txt b/media2/common/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..91a34fe
--- /dev/null
+++ b/media2/common/api/1.1.0-beta01.txt
@@ -0,0 +1,273 @@
+// Signature format: 3.0
+package androidx.media2.common {
+
+  public class CallbackMediaItem extends androidx.media2.common.MediaItem implements androidx.versionedparcelable.VersionedParcelable {
+    method public androidx.media2.common.DataSourceCallback getDataSourceCallback();
+  }
+
+  public static final class CallbackMediaItem.Builder extends androidx.media2.common.MediaItem.Builder {
+    ctor public CallbackMediaItem.Builder(androidx.media2.common.DataSourceCallback);
+    method public androidx.media2.common.CallbackMediaItem build();
+    method public androidx.media2.common.CallbackMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.common.CallbackMediaItem.Builder setMetadata(androidx.media2.common.MediaMetadata?);
+    method public androidx.media2.common.CallbackMediaItem.Builder setStartPosition(long);
+  }
+
+  public abstract class DataSourceCallback implements java.io.Closeable {
+    ctor public DataSourceCallback();
+    method public abstract long getSize() throws java.io.IOException;
+    method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
+  }
+
+  public class FileMediaItem extends androidx.media2.common.MediaItem implements androidx.versionedparcelable.VersionedParcelable {
+    method public long getFileDescriptorLength();
+    method public long getFileDescriptorOffset();
+    method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
+    field public static final long FD_LENGTH_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
+  }
+
+  public static final class FileMediaItem.Builder extends androidx.media2.common.MediaItem.Builder {
+    ctor public FileMediaItem.Builder(android.os.ParcelFileDescriptor);
+    method public androidx.media2.common.FileMediaItem build();
+    method public androidx.media2.common.FileMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.common.FileMediaItem.Builder setFileDescriptorLength(long);
+    method public androidx.media2.common.FileMediaItem.Builder setFileDescriptorOffset(long);
+    method public androidx.media2.common.FileMediaItem.Builder setMetadata(androidx.media2.common.MediaMetadata?);
+    method public androidx.media2.common.FileMediaItem.Builder setStartPosition(long);
+  }
+
+  public class MediaItem implements androidx.versionedparcelable.VersionedParcelable {
+    method public long getEndPosition();
+    method public androidx.media2.common.MediaMetadata? getMetadata();
+    method public long getStartPosition();
+    method public void setMetadata(androidx.media2.common.MediaMetadata?);
+    field public static final long POSITION_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
+  }
+
+  public static class MediaItem.Builder {
+    ctor public MediaItem.Builder();
+    method public androidx.media2.common.MediaItem build();
+    method public androidx.media2.common.MediaItem.Builder setEndPosition(long);
+    method public androidx.media2.common.MediaItem.Builder setMetadata(androidx.media2.common.MediaMetadata?);
+    method public androidx.media2.common.MediaItem.Builder setStartPosition(long);
+  }
+
+  public final class MediaMetadata implements androidx.versionedparcelable.VersionedParcelable {
+    method public boolean containsKey(String);
+    method public android.graphics.Bitmap? getBitmap(String);
+    method public android.os.Bundle? getExtras();
+    method public float getFloat(String);
+    method public long getLong(String);
+    method public String? getMediaId();
+    method public androidx.media2.common.Rating? getRating(String);
+    method public String? getString(String);
+    method public CharSequence? getText(String);
+    method public java.util.Set<java.lang.String!> keySet();
+    method public int size();
+    field public static final long BROWSABLE_TYPE_ALBUMS = 2L; // 0x2L
+    field public static final long BROWSABLE_TYPE_ARTISTS = 3L; // 0x3L
+    field public static final long BROWSABLE_TYPE_GENRES = 4L; // 0x4L
+    field public static final long BROWSABLE_TYPE_MIXED = 0L; // 0x0L
+    field public static final long BROWSABLE_TYPE_NONE = -1L; // 0xffffffffffffffffL
+    field public static final long BROWSABLE_TYPE_PLAYLISTS = 5L; // 0x5L
+    field public static final long BROWSABLE_TYPE_TITLES = 1L; // 0x1L
+    field public static final long BROWSABLE_TYPE_YEARS = 6L; // 0x6L
+    field public static final String METADATA_KEY_ADVERTISEMENT = "androidx.media2.metadata.ADVERTISEMENT";
+    field public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+    field public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+    field public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+    field public static final String METADATA_KEY_ART = "android.media.metadata.ART";
+    field public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+    field public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final String METADATA_KEY_BROWSABLE = "androidx.media2.metadata.BROWSABLE";
+    field public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+    field public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
+    field public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+    field public static final String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+    field public static final String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+    field public static final String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+    field public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+    field public static final String METADATA_KEY_DOWNLOAD_STATUS = "androidx.media2.metadata.DOWNLOAD_STATUS";
+    field public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final String METADATA_KEY_EXTRAS = "androidx.media2.metadata.EXTRAS";
+    field public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+    field public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+    field public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+    field public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+    field public static final String METADATA_KEY_PLAYABLE = "androidx.media2.metadata.PLAYABLE";
+    field public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
+    field public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+    field public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+    field public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+    field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
+    field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
+    field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
+  }
+
+  public static final class MediaMetadata.Builder {
+    ctor public MediaMetadata.Builder();
+    ctor public MediaMetadata.Builder(androidx.media2.common.MediaMetadata);
+    method public androidx.media2.common.MediaMetadata build();
+    method public androidx.media2.common.MediaMetadata.Builder putBitmap(String, android.graphics.Bitmap?);
+    method public androidx.media2.common.MediaMetadata.Builder putFloat(String, float);
+    method public androidx.media2.common.MediaMetadata.Builder putLong(String, long);
+    method public androidx.media2.common.MediaMetadata.Builder putRating(String, androidx.media2.common.Rating?);
+    method public androidx.media2.common.MediaMetadata.Builder putString(String, String?);
+    method public androidx.media2.common.MediaMetadata.Builder putText(String, CharSequence?);
+    method public androidx.media2.common.MediaMetadata.Builder setExtras(android.os.Bundle?);
+  }
+
+  public interface Rating extends androidx.versionedparcelable.VersionedParcelable {
+    method public boolean isRated();
+  }
+
+  public abstract class SessionPlayer implements java.io.Closeable {
+    ctor public SessionPlayer();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> addPlaylistItem(int, androidx.media2.common.MediaItem);
+    method @CallSuper public void close();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> deselectTrack(androidx.media2.common.SessionPlayer.TrackInfo);
+    method public abstract androidx.media.AudioAttributesCompat? getAudioAttributes();
+    method public abstract long getBufferedPosition();
+    method public abstract int getBufferingState();
+    method protected final java.util.List<androidx.core.util.Pair<androidx.media2.common.SessionPlayer.PlayerCallback!,java.util.concurrent.Executor!>!> getCallbacks();
+    method public abstract androidx.media2.common.MediaItem? getCurrentMediaItem();
+    method @IntRange(from=androidx.media2.common.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getCurrentMediaItemIndex();
+    method public abstract long getCurrentPosition();
+    method public abstract long getDuration();
+    method @IntRange(from=androidx.media2.common.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getNextMediaItemIndex();
+    method public abstract float getPlaybackSpeed();
+    method public abstract int getPlayerState();
+    method public abstract java.util.List<androidx.media2.common.MediaItem!>? getPlaylist();
+    method public abstract androidx.media2.common.MediaMetadata? getPlaylistMetadata();
+    method @IntRange(from=androidx.media2.common.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getPreviousMediaItemIndex();
+    method public abstract int getRepeatMode();
+    method public androidx.media2.common.SessionPlayer.TrackInfo? getSelectedTrack(int);
+    method public abstract int getShuffleMode();
+    method public java.util.List<androidx.media2.common.SessionPlayer.TrackInfo!> getTracks();
+    method public androidx.media2.common.VideoSize getVideoSize();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> movePlaylistItem(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> pause();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> play();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> prepare();
+    method public final void registerPlayerCallback(java.util.concurrent.Executor, androidx.media2.common.SessionPlayer.PlayerCallback);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> removePlaylistItem(@IntRange(from=0) int);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> replacePlaylistItem(int, androidx.media2.common.MediaItem);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> seekTo(long);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> selectTrack(androidx.media2.common.SessionPlayer.TrackInfo);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setAudioAttributes(androidx.media.AudioAttributesCompat);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setMediaItem(androidx.media2.common.MediaItem);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaybackSpeed(float);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaylist(java.util.List<androidx.media2.common.MediaItem!>, androidx.media2.common.MediaMetadata?);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setRepeatMode(int);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setShuffleMode(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setSurface(android.view.Surface?);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToNextPlaylistItem();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToPlaylistItem(@IntRange(from=0) int);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToPreviousPlaylistItem();
+    method public final void unregisterPlayerCallback(androidx.media2.common.SessionPlayer.PlayerCallback);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> updatePlaylistMetadata(androidx.media2.common.MediaMetadata?);
+    field public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1; // 0x1
+    field public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2; // 0x2
+    field public static final int BUFFERING_STATE_COMPLETE = 3; // 0x3
+    field public static final int BUFFERING_STATE_UNKNOWN = 0; // 0x0
+    field public static final int INVALID_ITEM_INDEX = -1; // 0xffffffff
+    field public static final int PLAYER_STATE_ERROR = 3; // 0x3
+    field public static final int PLAYER_STATE_IDLE = 0; // 0x0
+    field public static final int PLAYER_STATE_PAUSED = 1; // 0x1
+    field public static final int PLAYER_STATE_PLAYING = 2; // 0x2
+    field public static final int REPEAT_MODE_ALL = 2; // 0x2
+    field public static final int REPEAT_MODE_GROUP = 3; // 0x3
+    field public static final int REPEAT_MODE_NONE = 0; // 0x0
+    field public static final int REPEAT_MODE_ONE = 1; // 0x1
+    field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
+    field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
+    field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
+    field public static final long UNKNOWN_TIME = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+  public abstract static class SessionPlayer.PlayerCallback {
+    ctor public SessionPlayer.PlayerCallback();
+    method public void onAudioAttributesChanged(androidx.media2.common.SessionPlayer, androidx.media.AudioAttributesCompat?);
+    method public void onBufferingStateChanged(androidx.media2.common.SessionPlayer, androidx.media2.common.MediaItem?, int);
+    method public void onCurrentMediaItemChanged(androidx.media2.common.SessionPlayer, androidx.media2.common.MediaItem);
+    method public void onPlaybackCompleted(androidx.media2.common.SessionPlayer);
+    method public void onPlaybackSpeedChanged(androidx.media2.common.SessionPlayer, float);
+    method public void onPlayerStateChanged(androidx.media2.common.SessionPlayer, int);
+    method public void onPlaylistChanged(androidx.media2.common.SessionPlayer, java.util.List<androidx.media2.common.MediaItem!>?, androidx.media2.common.MediaMetadata?);
+    method public void onPlaylistMetadataChanged(androidx.media2.common.SessionPlayer, androidx.media2.common.MediaMetadata?);
+    method public void onRepeatModeChanged(androidx.media2.common.SessionPlayer, int);
+    method public void onSeekCompleted(androidx.media2.common.SessionPlayer, long);
+    method public void onShuffleModeChanged(androidx.media2.common.SessionPlayer, int);
+    method public void onSubtitleData(androidx.media2.common.SessionPlayer, androidx.media2.common.MediaItem, androidx.media2.common.SessionPlayer.TrackInfo, androidx.media2.common.SubtitleData);
+    method public void onTrackDeselected(androidx.media2.common.SessionPlayer, androidx.media2.common.SessionPlayer.TrackInfo);
+    method public void onTrackSelected(androidx.media2.common.SessionPlayer, androidx.media2.common.SessionPlayer.TrackInfo);
+    method public void onTracksChanged(androidx.media2.common.SessionPlayer, java.util.List<androidx.media2.common.SessionPlayer.TrackInfo!>);
+    method public void onVideoSizeChanged(androidx.media2.common.SessionPlayer, androidx.media2.common.VideoSize);
+  }
+
+  public static class SessionPlayer.PlayerResult {
+    ctor public SessionPlayer.PlayerResult(int, androidx.media2.common.MediaItem?);
+    method public long getCompletionTime();
+    method public androidx.media2.common.MediaItem? getMediaItem();
+    method public int getResultCode();
+    field public static final int RESULT_ERROR_BAD_VALUE = -3; // 0xfffffffd
+    field public static final int RESULT_ERROR_INVALID_STATE = -2; // 0xfffffffe
+    field public static final int RESULT_ERROR_IO = -5; // 0xfffffffb
+    field public static final int RESULT_ERROR_NOT_SUPPORTED = -6; // 0xfffffffa
+    field public static final int RESULT_ERROR_PERMISSION_DENIED = -4; // 0xfffffffc
+    field public static final int RESULT_ERROR_UNKNOWN = -1; // 0xffffffff
+    field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  public static class SessionPlayer.TrackInfo implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SessionPlayer.TrackInfo(int, int, android.media.MediaFormat?);
+    ctor public SessionPlayer.TrackInfo(int, int, android.media.MediaFormat?, boolean);
+    method public android.media.MediaFormat? getFormat();
+    method public int getId();
+    method public java.util.Locale getLanguage();
+    method public int getTrackType();
+    method public boolean isSelectable();
+    field public static final int MEDIA_TRACK_TYPE_AUDIO = 2; // 0x2
+    field public static final int MEDIA_TRACK_TYPE_METADATA = 5; // 0x5
+    field public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4; // 0x4
+    field public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
+  }
+
+  public final class SubtitleData implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SubtitleData(long, long, byte[]);
+    method public byte[] getData();
+    method public long getDurationUs();
+    method public long getStartTimeUs();
+  }
+
+  public class UriMediaItem extends androidx.media2.common.MediaItem implements androidx.versionedparcelable.VersionedParcelable {
+    method public android.net.Uri getUri();
+    method public java.util.List<java.net.HttpCookie!>? getUriCookies();
+    method public java.util.Map<java.lang.String!,java.lang.String!>? getUriHeaders();
+  }
+
+  public static final class UriMediaItem.Builder extends androidx.media2.common.MediaItem.Builder {
+    ctor public UriMediaItem.Builder(android.net.Uri);
+    ctor public UriMediaItem.Builder(android.net.Uri, java.util.Map<java.lang.String!,java.lang.String!>?, java.util.List<java.net.HttpCookie!>?);
+    method public androidx.media2.common.UriMediaItem build();
+    method public androidx.media2.common.UriMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.common.UriMediaItem.Builder setMetadata(androidx.media2.common.MediaMetadata?);
+    method public androidx.media2.common.UriMediaItem.Builder setStartPosition(long);
+  }
+
+  public class VideoSize implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public VideoSize(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @IntRange(from=0) public int getHeight();
+    method @IntRange(from=0) public int getWidth();
+  }
+
+}
+
diff --git a/media2/common/api/public_plus_experimental_1.1.0-beta01.txt b/media2/common/api/public_plus_experimental_1.1.0-beta01.txt
new file mode 100644
index 0000000..ecd925d
--- /dev/null
+++ b/media2/common/api/public_plus_experimental_1.1.0-beta01.txt
@@ -0,0 +1,273 @@
+// Signature format: 3.0
+package androidx.media2.common {
+
+  public class CallbackMediaItem extends androidx.media2.common.MediaItem {
+    method public androidx.media2.common.DataSourceCallback getDataSourceCallback();
+  }
+
+  public static final class CallbackMediaItem.Builder extends androidx.media2.common.MediaItem.Builder {
+    ctor public CallbackMediaItem.Builder(androidx.media2.common.DataSourceCallback);
+    method public androidx.media2.common.CallbackMediaItem build();
+    method public androidx.media2.common.CallbackMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.common.CallbackMediaItem.Builder setMetadata(androidx.media2.common.MediaMetadata?);
+    method public androidx.media2.common.CallbackMediaItem.Builder setStartPosition(long);
+  }
+
+  public abstract class DataSourceCallback implements java.io.Closeable {
+    ctor public DataSourceCallback();
+    method public abstract long getSize() throws java.io.IOException;
+    method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
+  }
+
+  public class FileMediaItem extends androidx.media2.common.MediaItem {
+    method public long getFileDescriptorLength();
+    method public long getFileDescriptorOffset();
+    method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
+    field public static final long FD_LENGTH_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
+  }
+
+  public static final class FileMediaItem.Builder extends androidx.media2.common.MediaItem.Builder {
+    ctor public FileMediaItem.Builder(android.os.ParcelFileDescriptor);
+    method public androidx.media2.common.FileMediaItem build();
+    method public androidx.media2.common.FileMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.common.FileMediaItem.Builder setFileDescriptorLength(long);
+    method public androidx.media2.common.FileMediaItem.Builder setFileDescriptorOffset(long);
+    method public androidx.media2.common.FileMediaItem.Builder setMetadata(androidx.media2.common.MediaMetadata?);
+    method public androidx.media2.common.FileMediaItem.Builder setStartPosition(long);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(isCustom=true) public class MediaItem extends androidx.versionedparcelable.CustomVersionedParcelable {
+    method public long getEndPosition();
+    method public androidx.media2.common.MediaMetadata? getMetadata();
+    method public long getStartPosition();
+    method public void setMetadata(androidx.media2.common.MediaMetadata?);
+    field public static final long POSITION_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
+  }
+
+  public static class MediaItem.Builder {
+    ctor public MediaItem.Builder();
+    method public androidx.media2.common.MediaItem build();
+    method public androidx.media2.common.MediaItem.Builder setEndPosition(long);
+    method public androidx.media2.common.MediaItem.Builder setMetadata(androidx.media2.common.MediaMetadata?);
+    method public androidx.media2.common.MediaItem.Builder setStartPosition(long);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(isCustom=true) public final class MediaMetadata extends androidx.versionedparcelable.CustomVersionedParcelable {
+    method public boolean containsKey(String);
+    method public android.graphics.Bitmap? getBitmap(String);
+    method public android.os.Bundle? getExtras();
+    method public float getFloat(String);
+    method public long getLong(String);
+    method public String? getMediaId();
+    method public androidx.media2.common.Rating? getRating(String);
+    method public String? getString(String);
+    method public CharSequence? getText(String);
+    method public java.util.Set<java.lang.String!> keySet();
+    method public int size();
+    field public static final long BROWSABLE_TYPE_ALBUMS = 2L; // 0x2L
+    field public static final long BROWSABLE_TYPE_ARTISTS = 3L; // 0x3L
+    field public static final long BROWSABLE_TYPE_GENRES = 4L; // 0x4L
+    field public static final long BROWSABLE_TYPE_MIXED = 0L; // 0x0L
+    field public static final long BROWSABLE_TYPE_NONE = -1L; // 0xffffffffffffffffL
+    field public static final long BROWSABLE_TYPE_PLAYLISTS = 5L; // 0x5L
+    field public static final long BROWSABLE_TYPE_TITLES = 1L; // 0x1L
+    field public static final long BROWSABLE_TYPE_YEARS = 6L; // 0x6L
+    field public static final String METADATA_KEY_ADVERTISEMENT = "androidx.media2.metadata.ADVERTISEMENT";
+    field public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+    field public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+    field public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+    field public static final String METADATA_KEY_ART = "android.media.metadata.ART";
+    field public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+    field public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final String METADATA_KEY_BROWSABLE = "androidx.media2.metadata.BROWSABLE";
+    field public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+    field public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
+    field public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+    field public static final String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+    field public static final String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+    field public static final String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+    field public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+    field public static final String METADATA_KEY_DOWNLOAD_STATUS = "androidx.media2.metadata.DOWNLOAD_STATUS";
+    field public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final String METADATA_KEY_EXTRAS = "androidx.media2.metadata.EXTRAS";
+    field public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+    field public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+    field public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+    field public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+    field public static final String METADATA_KEY_PLAYABLE = "androidx.media2.metadata.PLAYABLE";
+    field public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
+    field public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+    field public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+    field public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+    field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
+    field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
+    field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
+  }
+
+  public static final class MediaMetadata.Builder {
+    ctor public MediaMetadata.Builder();
+    ctor public MediaMetadata.Builder(androidx.media2.common.MediaMetadata);
+    method public androidx.media2.common.MediaMetadata build();
+    method public androidx.media2.common.MediaMetadata.Builder putBitmap(String, android.graphics.Bitmap?);
+    method public androidx.media2.common.MediaMetadata.Builder putFloat(String, float);
+    method public androidx.media2.common.MediaMetadata.Builder putLong(String, long);
+    method public androidx.media2.common.MediaMetadata.Builder putRating(String, androidx.media2.common.Rating?);
+    method public androidx.media2.common.MediaMetadata.Builder putString(String, String?);
+    method public androidx.media2.common.MediaMetadata.Builder putText(String, CharSequence?);
+    method public androidx.media2.common.MediaMetadata.Builder setExtras(android.os.Bundle?);
+  }
+
+  public interface Rating extends androidx.versionedparcelable.VersionedParcelable {
+    method public boolean isRated();
+  }
+
+  public abstract class SessionPlayer implements java.io.Closeable {
+    ctor public SessionPlayer();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> addPlaylistItem(int, androidx.media2.common.MediaItem);
+    method @CallSuper public void close();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> deselectTrack(androidx.media2.common.SessionPlayer.TrackInfo);
+    method public abstract androidx.media.AudioAttributesCompat? getAudioAttributes();
+    method public abstract long getBufferedPosition();
+    method public abstract int getBufferingState();
+    method protected final java.util.List<androidx.core.util.Pair<androidx.media2.common.SessionPlayer.PlayerCallback!,java.util.concurrent.Executor!>!> getCallbacks();
+    method public abstract androidx.media2.common.MediaItem? getCurrentMediaItem();
+    method @IntRange(from=androidx.media2.common.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getCurrentMediaItemIndex();
+    method public abstract long getCurrentPosition();
+    method public abstract long getDuration();
+    method @IntRange(from=androidx.media2.common.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getNextMediaItemIndex();
+    method public abstract float getPlaybackSpeed();
+    method public abstract int getPlayerState();
+    method public abstract java.util.List<androidx.media2.common.MediaItem!>? getPlaylist();
+    method public abstract androidx.media2.common.MediaMetadata? getPlaylistMetadata();
+    method @IntRange(from=androidx.media2.common.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getPreviousMediaItemIndex();
+    method public abstract int getRepeatMode();
+    method public androidx.media2.common.SessionPlayer.TrackInfo? getSelectedTrack(int);
+    method public abstract int getShuffleMode();
+    method public java.util.List<androidx.media2.common.SessionPlayer.TrackInfo!> getTracks();
+    method public androidx.media2.common.VideoSize getVideoSize();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> movePlaylistItem(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> pause();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> play();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> prepare();
+    method public final void registerPlayerCallback(java.util.concurrent.Executor, androidx.media2.common.SessionPlayer.PlayerCallback);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> removePlaylistItem(@IntRange(from=0) int);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> replacePlaylistItem(int, androidx.media2.common.MediaItem);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> seekTo(long);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> selectTrack(androidx.media2.common.SessionPlayer.TrackInfo);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setAudioAttributes(androidx.media.AudioAttributesCompat);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setMediaItem(androidx.media2.common.MediaItem);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaybackSpeed(float);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaylist(java.util.List<androidx.media2.common.MediaItem!>, androidx.media2.common.MediaMetadata?);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setRepeatMode(int);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setShuffleMode(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setSurface(android.view.Surface?);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToNextPlaylistItem();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToPlaylistItem(@IntRange(from=0) int);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToPreviousPlaylistItem();
+    method public final void unregisterPlayerCallback(androidx.media2.common.SessionPlayer.PlayerCallback);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> updatePlaylistMetadata(androidx.media2.common.MediaMetadata?);
+    field public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1; // 0x1
+    field public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2; // 0x2
+    field public static final int BUFFERING_STATE_COMPLETE = 3; // 0x3
+    field public static final int BUFFERING_STATE_UNKNOWN = 0; // 0x0
+    field public static final int INVALID_ITEM_INDEX = -1; // 0xffffffff
+    field public static final int PLAYER_STATE_ERROR = 3; // 0x3
+    field public static final int PLAYER_STATE_IDLE = 0; // 0x0
+    field public static final int PLAYER_STATE_PAUSED = 1; // 0x1
+    field public static final int PLAYER_STATE_PLAYING = 2; // 0x2
+    field public static final int REPEAT_MODE_ALL = 2; // 0x2
+    field public static final int REPEAT_MODE_GROUP = 3; // 0x3
+    field public static final int REPEAT_MODE_NONE = 0; // 0x0
+    field public static final int REPEAT_MODE_ONE = 1; // 0x1
+    field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
+    field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
+    field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
+    field public static final long UNKNOWN_TIME = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+  public abstract static class SessionPlayer.PlayerCallback {
+    ctor public SessionPlayer.PlayerCallback();
+    method public void onAudioAttributesChanged(androidx.media2.common.SessionPlayer, androidx.media.AudioAttributesCompat?);
+    method public void onBufferingStateChanged(androidx.media2.common.SessionPlayer, androidx.media2.common.MediaItem?, int);
+    method public void onCurrentMediaItemChanged(androidx.media2.common.SessionPlayer, androidx.media2.common.MediaItem);
+    method public void onPlaybackCompleted(androidx.media2.common.SessionPlayer);
+    method public void onPlaybackSpeedChanged(androidx.media2.common.SessionPlayer, float);
+    method public void onPlayerStateChanged(androidx.media2.common.SessionPlayer, int);
+    method public void onPlaylistChanged(androidx.media2.common.SessionPlayer, java.util.List<androidx.media2.common.MediaItem!>?, androidx.media2.common.MediaMetadata?);
+    method public void onPlaylistMetadataChanged(androidx.media2.common.SessionPlayer, androidx.media2.common.MediaMetadata?);
+    method public void onRepeatModeChanged(androidx.media2.common.SessionPlayer, int);
+    method public void onSeekCompleted(androidx.media2.common.SessionPlayer, long);
+    method public void onShuffleModeChanged(androidx.media2.common.SessionPlayer, int);
+    method public void onSubtitleData(androidx.media2.common.SessionPlayer, androidx.media2.common.MediaItem, androidx.media2.common.SessionPlayer.TrackInfo, androidx.media2.common.SubtitleData);
+    method public void onTrackDeselected(androidx.media2.common.SessionPlayer, androidx.media2.common.SessionPlayer.TrackInfo);
+    method public void onTrackSelected(androidx.media2.common.SessionPlayer, androidx.media2.common.SessionPlayer.TrackInfo);
+    method public void onTracksChanged(androidx.media2.common.SessionPlayer, java.util.List<androidx.media2.common.SessionPlayer.TrackInfo!>);
+    method public void onVideoSizeChanged(androidx.media2.common.SessionPlayer, androidx.media2.common.VideoSize);
+  }
+
+  public static class SessionPlayer.PlayerResult {
+    ctor public SessionPlayer.PlayerResult(int, androidx.media2.common.MediaItem?);
+    method public long getCompletionTime();
+    method public androidx.media2.common.MediaItem? getMediaItem();
+    method public int getResultCode();
+    field public static final int RESULT_ERROR_BAD_VALUE = -3; // 0xfffffffd
+    field public static final int RESULT_ERROR_INVALID_STATE = -2; // 0xfffffffe
+    field public static final int RESULT_ERROR_IO = -5; // 0xfffffffb
+    field public static final int RESULT_ERROR_NOT_SUPPORTED = -6; // 0xfffffffa
+    field public static final int RESULT_ERROR_PERMISSION_DENIED = -4; // 0xfffffffc
+    field public static final int RESULT_ERROR_UNKNOWN = -1; // 0xffffffff
+    field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(isCustom=true) public static class SessionPlayer.TrackInfo extends androidx.versionedparcelable.CustomVersionedParcelable {
+    ctor public SessionPlayer.TrackInfo(int, int, android.media.MediaFormat?);
+    ctor public SessionPlayer.TrackInfo(int, int, android.media.MediaFormat?, boolean);
+    method public android.media.MediaFormat? getFormat();
+    method public int getId();
+    method public java.util.Locale getLanguage();
+    method public int getTrackType();
+    method public boolean isSelectable();
+    field public static final int MEDIA_TRACK_TYPE_AUDIO = 2; // 0x2
+    field public static final int MEDIA_TRACK_TYPE_METADATA = 5; // 0x5
+    field public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4; // 0x4
+    field public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class SubtitleData implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SubtitleData(long, long, byte[]);
+    method public byte[] getData();
+    method public long getDurationUs();
+    method public long getStartTimeUs();
+  }
+
+  public class UriMediaItem extends androidx.media2.common.MediaItem {
+    method public android.net.Uri getUri();
+    method public java.util.List<java.net.HttpCookie!>? getUriCookies();
+    method public java.util.Map<java.lang.String!,java.lang.String!>? getUriHeaders();
+  }
+
+  public static final class UriMediaItem.Builder extends androidx.media2.common.MediaItem.Builder {
+    ctor public UriMediaItem.Builder(android.net.Uri);
+    ctor public UriMediaItem.Builder(android.net.Uri, java.util.Map<java.lang.String!,java.lang.String!>?, java.util.List<java.net.HttpCookie!>?);
+    method public androidx.media2.common.UriMediaItem build();
+    method public androidx.media2.common.UriMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.common.UriMediaItem.Builder setMetadata(androidx.media2.common.MediaMetadata?);
+    method public androidx.media2.common.UriMediaItem.Builder setStartPosition(long);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public class VideoSize implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public VideoSize(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @IntRange(from=0) public int getHeight();
+    method @IntRange(from=0) public int getWidth();
+  }
+
+}
+
diff --git a/media2/common/api/res-1.1.0-beta01.txt b/media2/common/api/res-1.1.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media2/common/api/res-1.1.0-beta01.txt
diff --git a/media2/common/api/restricted_1.1.0-beta01.txt b/media2/common/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..ecd925d
--- /dev/null
+++ b/media2/common/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,273 @@
+// Signature format: 3.0
+package androidx.media2.common {
+
+  public class CallbackMediaItem extends androidx.media2.common.MediaItem {
+    method public androidx.media2.common.DataSourceCallback getDataSourceCallback();
+  }
+
+  public static final class CallbackMediaItem.Builder extends androidx.media2.common.MediaItem.Builder {
+    ctor public CallbackMediaItem.Builder(androidx.media2.common.DataSourceCallback);
+    method public androidx.media2.common.CallbackMediaItem build();
+    method public androidx.media2.common.CallbackMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.common.CallbackMediaItem.Builder setMetadata(androidx.media2.common.MediaMetadata?);
+    method public androidx.media2.common.CallbackMediaItem.Builder setStartPosition(long);
+  }
+
+  public abstract class DataSourceCallback implements java.io.Closeable {
+    ctor public DataSourceCallback();
+    method public abstract long getSize() throws java.io.IOException;
+    method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
+  }
+
+  public class FileMediaItem extends androidx.media2.common.MediaItem {
+    method public long getFileDescriptorLength();
+    method public long getFileDescriptorOffset();
+    method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
+    field public static final long FD_LENGTH_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
+  }
+
+  public static final class FileMediaItem.Builder extends androidx.media2.common.MediaItem.Builder {
+    ctor public FileMediaItem.Builder(android.os.ParcelFileDescriptor);
+    method public androidx.media2.common.FileMediaItem build();
+    method public androidx.media2.common.FileMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.common.FileMediaItem.Builder setFileDescriptorLength(long);
+    method public androidx.media2.common.FileMediaItem.Builder setFileDescriptorOffset(long);
+    method public androidx.media2.common.FileMediaItem.Builder setMetadata(androidx.media2.common.MediaMetadata?);
+    method public androidx.media2.common.FileMediaItem.Builder setStartPosition(long);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(isCustom=true) public class MediaItem extends androidx.versionedparcelable.CustomVersionedParcelable {
+    method public long getEndPosition();
+    method public androidx.media2.common.MediaMetadata? getMetadata();
+    method public long getStartPosition();
+    method public void setMetadata(androidx.media2.common.MediaMetadata?);
+    field public static final long POSITION_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
+  }
+
+  public static class MediaItem.Builder {
+    ctor public MediaItem.Builder();
+    method public androidx.media2.common.MediaItem build();
+    method public androidx.media2.common.MediaItem.Builder setEndPosition(long);
+    method public androidx.media2.common.MediaItem.Builder setMetadata(androidx.media2.common.MediaMetadata?);
+    method public androidx.media2.common.MediaItem.Builder setStartPosition(long);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(isCustom=true) public final class MediaMetadata extends androidx.versionedparcelable.CustomVersionedParcelable {
+    method public boolean containsKey(String);
+    method public android.graphics.Bitmap? getBitmap(String);
+    method public android.os.Bundle? getExtras();
+    method public float getFloat(String);
+    method public long getLong(String);
+    method public String? getMediaId();
+    method public androidx.media2.common.Rating? getRating(String);
+    method public String? getString(String);
+    method public CharSequence? getText(String);
+    method public java.util.Set<java.lang.String!> keySet();
+    method public int size();
+    field public static final long BROWSABLE_TYPE_ALBUMS = 2L; // 0x2L
+    field public static final long BROWSABLE_TYPE_ARTISTS = 3L; // 0x3L
+    field public static final long BROWSABLE_TYPE_GENRES = 4L; // 0x4L
+    field public static final long BROWSABLE_TYPE_MIXED = 0L; // 0x0L
+    field public static final long BROWSABLE_TYPE_NONE = -1L; // 0xffffffffffffffffL
+    field public static final long BROWSABLE_TYPE_PLAYLISTS = 5L; // 0x5L
+    field public static final long BROWSABLE_TYPE_TITLES = 1L; // 0x1L
+    field public static final long BROWSABLE_TYPE_YEARS = 6L; // 0x6L
+    field public static final String METADATA_KEY_ADVERTISEMENT = "androidx.media2.metadata.ADVERTISEMENT";
+    field public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+    field public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+    field public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+    field public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+    field public static final String METADATA_KEY_ART = "android.media.metadata.ART";
+    field public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+    field public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+    field public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+    field public static final String METADATA_KEY_BROWSABLE = "androidx.media2.metadata.BROWSABLE";
+    field public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
+    field public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+    field public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
+    field public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+    field public static final String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+    field public static final String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+    field public static final String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+    field public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+    field public static final String METADATA_KEY_DOWNLOAD_STATUS = "androidx.media2.metadata.DOWNLOAD_STATUS";
+    field public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+    field public static final String METADATA_KEY_EXTRAS = "androidx.media2.metadata.EXTRAS";
+    field public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+    field public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
+    field public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
+    field public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+    field public static final String METADATA_KEY_PLAYABLE = "androidx.media2.metadata.PLAYABLE";
+    field public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
+    field public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+    field public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+    field public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+    field public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+    field public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+    field public static final long STATUS_DOWNLOADED = 2L; // 0x2L
+    field public static final long STATUS_DOWNLOADING = 1L; // 0x1L
+    field public static final long STATUS_NOT_DOWNLOADED = 0L; // 0x0L
+  }
+
+  public static final class MediaMetadata.Builder {
+    ctor public MediaMetadata.Builder();
+    ctor public MediaMetadata.Builder(androidx.media2.common.MediaMetadata);
+    method public androidx.media2.common.MediaMetadata build();
+    method public androidx.media2.common.MediaMetadata.Builder putBitmap(String, android.graphics.Bitmap?);
+    method public androidx.media2.common.MediaMetadata.Builder putFloat(String, float);
+    method public androidx.media2.common.MediaMetadata.Builder putLong(String, long);
+    method public androidx.media2.common.MediaMetadata.Builder putRating(String, androidx.media2.common.Rating?);
+    method public androidx.media2.common.MediaMetadata.Builder putString(String, String?);
+    method public androidx.media2.common.MediaMetadata.Builder putText(String, CharSequence?);
+    method public androidx.media2.common.MediaMetadata.Builder setExtras(android.os.Bundle?);
+  }
+
+  public interface Rating extends androidx.versionedparcelable.VersionedParcelable {
+    method public boolean isRated();
+  }
+
+  public abstract class SessionPlayer implements java.io.Closeable {
+    ctor public SessionPlayer();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> addPlaylistItem(int, androidx.media2.common.MediaItem);
+    method @CallSuper public void close();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> deselectTrack(androidx.media2.common.SessionPlayer.TrackInfo);
+    method public abstract androidx.media.AudioAttributesCompat? getAudioAttributes();
+    method public abstract long getBufferedPosition();
+    method public abstract int getBufferingState();
+    method protected final java.util.List<androidx.core.util.Pair<androidx.media2.common.SessionPlayer.PlayerCallback!,java.util.concurrent.Executor!>!> getCallbacks();
+    method public abstract androidx.media2.common.MediaItem? getCurrentMediaItem();
+    method @IntRange(from=androidx.media2.common.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getCurrentMediaItemIndex();
+    method public abstract long getCurrentPosition();
+    method public abstract long getDuration();
+    method @IntRange(from=androidx.media2.common.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getNextMediaItemIndex();
+    method public abstract float getPlaybackSpeed();
+    method public abstract int getPlayerState();
+    method public abstract java.util.List<androidx.media2.common.MediaItem!>? getPlaylist();
+    method public abstract androidx.media2.common.MediaMetadata? getPlaylistMetadata();
+    method @IntRange(from=androidx.media2.common.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getPreviousMediaItemIndex();
+    method public abstract int getRepeatMode();
+    method public androidx.media2.common.SessionPlayer.TrackInfo? getSelectedTrack(int);
+    method public abstract int getShuffleMode();
+    method public java.util.List<androidx.media2.common.SessionPlayer.TrackInfo!> getTracks();
+    method public androidx.media2.common.VideoSize getVideoSize();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> movePlaylistItem(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> pause();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> play();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> prepare();
+    method public final void registerPlayerCallback(java.util.concurrent.Executor, androidx.media2.common.SessionPlayer.PlayerCallback);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> removePlaylistItem(@IntRange(from=0) int);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> replacePlaylistItem(int, androidx.media2.common.MediaItem);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> seekTo(long);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> selectTrack(androidx.media2.common.SessionPlayer.TrackInfo);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setAudioAttributes(androidx.media.AudioAttributesCompat);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setMediaItem(androidx.media2.common.MediaItem);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaybackSpeed(float);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaylist(java.util.List<androidx.media2.common.MediaItem!>, androidx.media2.common.MediaMetadata?);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setRepeatMode(int);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setShuffleMode(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setSurface(android.view.Surface?);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToNextPlaylistItem();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToPlaylistItem(@IntRange(from=0) int);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToPreviousPlaylistItem();
+    method public final void unregisterPlayerCallback(androidx.media2.common.SessionPlayer.PlayerCallback);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> updatePlaylistMetadata(androidx.media2.common.MediaMetadata?);
+    field public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1; // 0x1
+    field public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2; // 0x2
+    field public static final int BUFFERING_STATE_COMPLETE = 3; // 0x3
+    field public static final int BUFFERING_STATE_UNKNOWN = 0; // 0x0
+    field public static final int INVALID_ITEM_INDEX = -1; // 0xffffffff
+    field public static final int PLAYER_STATE_ERROR = 3; // 0x3
+    field public static final int PLAYER_STATE_IDLE = 0; // 0x0
+    field public static final int PLAYER_STATE_PAUSED = 1; // 0x1
+    field public static final int PLAYER_STATE_PLAYING = 2; // 0x2
+    field public static final int REPEAT_MODE_ALL = 2; // 0x2
+    field public static final int REPEAT_MODE_GROUP = 3; // 0x3
+    field public static final int REPEAT_MODE_NONE = 0; // 0x0
+    field public static final int REPEAT_MODE_ONE = 1; // 0x1
+    field public static final int SHUFFLE_MODE_ALL = 1; // 0x1
+    field public static final int SHUFFLE_MODE_GROUP = 2; // 0x2
+    field public static final int SHUFFLE_MODE_NONE = 0; // 0x0
+    field public static final long UNKNOWN_TIME = -9223372036854775808L; // 0x8000000000000000L
+  }
+
+  public abstract static class SessionPlayer.PlayerCallback {
+    ctor public SessionPlayer.PlayerCallback();
+    method public void onAudioAttributesChanged(androidx.media2.common.SessionPlayer, androidx.media.AudioAttributesCompat?);
+    method public void onBufferingStateChanged(androidx.media2.common.SessionPlayer, androidx.media2.common.MediaItem?, int);
+    method public void onCurrentMediaItemChanged(androidx.media2.common.SessionPlayer, androidx.media2.common.MediaItem);
+    method public void onPlaybackCompleted(androidx.media2.common.SessionPlayer);
+    method public void onPlaybackSpeedChanged(androidx.media2.common.SessionPlayer, float);
+    method public void onPlayerStateChanged(androidx.media2.common.SessionPlayer, int);
+    method public void onPlaylistChanged(androidx.media2.common.SessionPlayer, java.util.List<androidx.media2.common.MediaItem!>?, androidx.media2.common.MediaMetadata?);
+    method public void onPlaylistMetadataChanged(androidx.media2.common.SessionPlayer, androidx.media2.common.MediaMetadata?);
+    method public void onRepeatModeChanged(androidx.media2.common.SessionPlayer, int);
+    method public void onSeekCompleted(androidx.media2.common.SessionPlayer, long);
+    method public void onShuffleModeChanged(androidx.media2.common.SessionPlayer, int);
+    method public void onSubtitleData(androidx.media2.common.SessionPlayer, androidx.media2.common.MediaItem, androidx.media2.common.SessionPlayer.TrackInfo, androidx.media2.common.SubtitleData);
+    method public void onTrackDeselected(androidx.media2.common.SessionPlayer, androidx.media2.common.SessionPlayer.TrackInfo);
+    method public void onTrackSelected(androidx.media2.common.SessionPlayer, androidx.media2.common.SessionPlayer.TrackInfo);
+    method public void onTracksChanged(androidx.media2.common.SessionPlayer, java.util.List<androidx.media2.common.SessionPlayer.TrackInfo!>);
+    method public void onVideoSizeChanged(androidx.media2.common.SessionPlayer, androidx.media2.common.VideoSize);
+  }
+
+  public static class SessionPlayer.PlayerResult {
+    ctor public SessionPlayer.PlayerResult(int, androidx.media2.common.MediaItem?);
+    method public long getCompletionTime();
+    method public androidx.media2.common.MediaItem? getMediaItem();
+    method public int getResultCode();
+    field public static final int RESULT_ERROR_BAD_VALUE = -3; // 0xfffffffd
+    field public static final int RESULT_ERROR_INVALID_STATE = -2; // 0xfffffffe
+    field public static final int RESULT_ERROR_IO = -5; // 0xfffffffb
+    field public static final int RESULT_ERROR_NOT_SUPPORTED = -6; // 0xfffffffa
+    field public static final int RESULT_ERROR_PERMISSION_DENIED = -4; // 0xfffffffc
+    field public static final int RESULT_ERROR_UNKNOWN = -1; // 0xffffffff
+    field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(isCustom=true) public static class SessionPlayer.TrackInfo extends androidx.versionedparcelable.CustomVersionedParcelable {
+    ctor public SessionPlayer.TrackInfo(int, int, android.media.MediaFormat?);
+    ctor public SessionPlayer.TrackInfo(int, int, android.media.MediaFormat?, boolean);
+    method public android.media.MediaFormat? getFormat();
+    method public int getId();
+    method public java.util.Locale getLanguage();
+    method public int getTrackType();
+    method public boolean isSelectable();
+    field public static final int MEDIA_TRACK_TYPE_AUDIO = 2; // 0x2
+    field public static final int MEDIA_TRACK_TYPE_METADATA = 5; // 0x5
+    field public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4; // 0x4
+    field public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class SubtitleData implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SubtitleData(long, long, byte[]);
+    method public byte[] getData();
+    method public long getDurationUs();
+    method public long getStartTimeUs();
+  }
+
+  public class UriMediaItem extends androidx.media2.common.MediaItem {
+    method public android.net.Uri getUri();
+    method public java.util.List<java.net.HttpCookie!>? getUriCookies();
+    method public java.util.Map<java.lang.String!,java.lang.String!>? getUriHeaders();
+  }
+
+  public static final class UriMediaItem.Builder extends androidx.media2.common.MediaItem.Builder {
+    ctor public UriMediaItem.Builder(android.net.Uri);
+    ctor public UriMediaItem.Builder(android.net.Uri, java.util.Map<java.lang.String!,java.lang.String!>?, java.util.List<java.net.HttpCookie!>?);
+    method public androidx.media2.common.UriMediaItem build();
+    method public androidx.media2.common.UriMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.common.UriMediaItem.Builder setMetadata(androidx.media2.common.MediaMetadata?);
+    method public androidx.media2.common.UriMediaItem.Builder setStartPosition(long);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public class VideoSize implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public VideoSize(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @IntRange(from=0) public int getHeight();
+    method @IntRange(from=0) public int getWidth();
+  }
+
+}
+
diff --git a/media2/media2-exoplayer/api/1.1.0-beta01.txt b/media2/media2-exoplayer/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..da4f6cc
--- /dev/null
+++ b/media2/media2-exoplayer/api/1.1.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 3.0
diff --git a/media2/media2-exoplayer/api/public_plus_experimental_1.1.0-beta01.txt b/media2/media2-exoplayer/api/public_plus_experimental_1.1.0-beta01.txt
new file mode 100644
index 0000000..da4f6cc
--- /dev/null
+++ b/media2/media2-exoplayer/api/public_plus_experimental_1.1.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 3.0
diff --git a/media2/media2-exoplayer/api/res-1.1.0-beta01.txt b/media2/media2-exoplayer/api/res-1.1.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media2/media2-exoplayer/api/res-1.1.0-beta01.txt
diff --git a/media2/media2-exoplayer/api/restricted_1.1.0-beta01.txt b/media2/media2-exoplayer/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..da4f6cc
--- /dev/null
+++ b/media2/media2-exoplayer/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 3.0
diff --git a/media2/player/api/1.1.0-beta01.txt b/media2/player/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..2aacb97
--- /dev/null
+++ b/media2/player/api/1.1.0-beta01.txt
@@ -0,0 +1,123 @@
+// Signature format: 3.0
+package androidx.media2.player {
+
+  public final class MediaPlayer extends androidx.media2.common.SessionPlayer {
+    ctor public MediaPlayer(android.content.Context);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> addPlaylistItem(int, androidx.media2.common.MediaItem);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> attachAuxEffect(int);
+    method public androidx.media.AudioAttributesCompat? getAudioAttributes();
+    method public int getAudioSessionId();
+    method public long getBufferedPosition();
+    method public int getBufferingState();
+    method public androidx.media2.common.MediaItem? getCurrentMediaItem();
+    method public int getCurrentMediaItemIndex();
+    method public long getCurrentPosition();
+    method public long getDuration();
+    method public float getMaxPlayerVolume();
+    method public int getNextMediaItemIndex();
+    method public androidx.media2.player.PlaybackParams getPlaybackParams();
+    method @FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE, fromInclusive=false) public float getPlaybackSpeed();
+    method public int getPlayerState();
+    method public float getPlayerVolume();
+    method public java.util.List<androidx.media2.common.MediaItem!>? getPlaylist();
+    method public androidx.media2.common.MediaMetadata? getPlaylistMetadata();
+    method public int getPreviousMediaItemIndex();
+    method public int getRepeatMode();
+    method public androidx.media2.player.MediaPlayer.TrackInfo? getSelectedTrack(int);
+    method public int getShuffleMode();
+    method public androidx.media2.player.MediaTimestamp? getTimestamp();
+    method @Deprecated public java.util.List<androidx.media2.player.MediaPlayer.TrackInfo!> getTrackInfo();
+    method public androidx.media2.player.VideoSize getVideoSize();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> pause();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> play();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> prepare();
+    method public void registerPlayerCallback(java.util.concurrent.Executor, androidx.media2.player.MediaPlayer.PlayerCallback);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> removePlaylistItem(@IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> replacePlaylistItem(int, androidx.media2.common.MediaItem);
+    method public void reset();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> seekTo(long);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> seekTo(long, int);
+    method @Deprecated public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> selectTrack(androidx.media2.player.MediaPlayer.TrackInfo);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setAudioAttributes(androidx.media.AudioAttributesCompat);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setAudioSessionId(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setAuxEffectSendLevel(@FloatRange(from=0, to=1) float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setMediaItem(androidx.media2.common.MediaItem);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaybackParams(androidx.media2.player.PlaybackParams);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaybackSpeed(@FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE, fromInclusive=false) float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlayerVolume(@FloatRange(from=0, to=1) float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaylist(java.util.List<androidx.media2.common.MediaItem!>, androidx.media2.common.MediaMetadata?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setRepeatMode(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setShuffleMode(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToNextPlaylistItem();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToPlaylistItem(@IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToPreviousPlaylistItem();
+    method public void unregisterPlayerCallback(androidx.media2.player.MediaPlayer.PlayerCallback);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> updatePlaylistMetadata(androidx.media2.common.MediaMetadata?);
+    field public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804; // 0x324
+    field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320
+    field public static final int MEDIA_INFO_BUFFERING_UPDATE = 704; // 0x2c0
+    field public static final int MEDIA_INFO_METADATA_UPDATE = 802; // 0x322
+    field public static final int MEDIA_INFO_NOT_SEEKABLE = 801; // 0x321
+    field public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; // 0x325
+    field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
+    field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
+    field @Deprecated public static final int NO_TRACK_SELECTED = -2147483648; // 0x80000000
+    field public static final int PLAYER_ERROR_IO = -1004; // 0xfffffc14
+    field public static final int PLAYER_ERROR_MALFORMED = -1007; // 0xfffffc11
+    field public static final int PLAYER_ERROR_TIMED_OUT = -110; // 0xffffff92
+    field public static final int PLAYER_ERROR_UNKNOWN = 1; // 0x1
+    field public static final int PLAYER_ERROR_UNSUPPORTED = -1010; // 0xfffffc0e
+    field public static final int SEEK_CLOSEST = 3; // 0x3
+    field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
+    field public static final int SEEK_NEXT_SYNC = 1; // 0x1
+    field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0
+  }
+
+  public abstract static class MediaPlayer.PlayerCallback extends androidx.media2.common.SessionPlayer.PlayerCallback {
+    ctor public MediaPlayer.PlayerCallback();
+    method public void onError(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, int, int);
+    method public void onInfo(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, int, int);
+    method public void onMediaTimeDiscontinuity(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, androidx.media2.player.MediaTimestamp);
+    method public void onTimedMetaDataAvailable(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, androidx.media2.player.TimedMetaData);
+    method @Deprecated public void onVideoSizeChanged(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, androidx.media2.player.VideoSize);
+  }
+
+  public static final class MediaPlayer.TrackInfo extends androidx.media2.common.SessionPlayer.TrackInfo implements androidx.versionedparcelable.VersionedParcelable {
+  }
+
+  public final class MediaTimestamp {
+    method public long getAnchorMediaTimeUs();
+    method public long getAnchorSystemNanoTime();
+    method public float getMediaClockRate();
+    field public static final androidx.media2.player.MediaTimestamp TIMESTAMP_UNKNOWN;
+  }
+
+  public final class PlaybackParams {
+    method public Integer? getAudioFallbackMode();
+    method public Float? getPitch();
+    method public Float? getSpeed();
+    field public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0; // 0x0
+    field public static final int AUDIO_FALLBACK_MODE_FAIL = 2; // 0x2
+    field public static final int AUDIO_FALLBACK_MODE_MUTE = 1; // 0x1
+  }
+
+  public static final class PlaybackParams.Builder {
+    ctor public PlaybackParams.Builder();
+    ctor public PlaybackParams.Builder(androidx.media2.player.PlaybackParams);
+    method public androidx.media2.player.PlaybackParams build();
+    method public androidx.media2.player.PlaybackParams.Builder setAudioFallbackMode(int);
+    method public androidx.media2.player.PlaybackParams.Builder setPitch(@FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE, fromInclusive=false) float);
+    method public androidx.media2.player.PlaybackParams.Builder setSpeed(@FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE, fromInclusive=false) float);
+  }
+
+  public class TimedMetaData {
+    method public byte[]! getMetaData();
+    method public long getTimestamp();
+  }
+
+  public final class VideoSize extends androidx.media2.common.VideoSize {
+    ctor public VideoSize(int, int);
+  }
+
+}
+
diff --git a/media2/player/api/public_plus_experimental_1.1.0-beta01.txt b/media2/player/api/public_plus_experimental_1.1.0-beta01.txt
new file mode 100644
index 0000000..8a75ae5
--- /dev/null
+++ b/media2/player/api/public_plus_experimental_1.1.0-beta01.txt
@@ -0,0 +1,123 @@
+// Signature format: 3.0
+package androidx.media2.player {
+
+  public final class MediaPlayer extends androidx.media2.common.SessionPlayer {
+    ctor public MediaPlayer(android.content.Context);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> addPlaylistItem(int, androidx.media2.common.MediaItem);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> attachAuxEffect(int);
+    method public androidx.media.AudioAttributesCompat? getAudioAttributes();
+    method public int getAudioSessionId();
+    method public long getBufferedPosition();
+    method @androidx.media2.common.SessionPlayer.BuffState public int getBufferingState();
+    method public androidx.media2.common.MediaItem? getCurrentMediaItem();
+    method public int getCurrentMediaItemIndex();
+    method public long getCurrentPosition();
+    method public long getDuration();
+    method public float getMaxPlayerVolume();
+    method public int getNextMediaItemIndex();
+    method public androidx.media2.player.PlaybackParams getPlaybackParams();
+    method @FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE, fromInclusive=false) public float getPlaybackSpeed();
+    method @androidx.media2.common.SessionPlayer.PlayerState public int getPlayerState();
+    method public float getPlayerVolume();
+    method public java.util.List<androidx.media2.common.MediaItem!>? getPlaylist();
+    method public androidx.media2.common.MediaMetadata? getPlaylistMetadata();
+    method public int getPreviousMediaItemIndex();
+    method public int getRepeatMode();
+    method public androidx.media2.player.MediaPlayer.TrackInfo? getSelectedTrack(int);
+    method public int getShuffleMode();
+    method public androidx.media2.player.MediaTimestamp? getTimestamp();
+    method @Deprecated public java.util.List<androidx.media2.player.MediaPlayer.TrackInfo!> getTrackInfo();
+    method public androidx.media2.player.VideoSize getVideoSize();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> pause();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> play();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> prepare();
+    method public void registerPlayerCallback(java.util.concurrent.Executor, androidx.media2.player.MediaPlayer.PlayerCallback);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> removePlaylistItem(@IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> replacePlaylistItem(int, androidx.media2.common.MediaItem);
+    method public void reset();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> seekTo(long);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> seekTo(long, int);
+    method @Deprecated public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> selectTrack(androidx.media2.player.MediaPlayer.TrackInfo);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setAudioAttributes(androidx.media.AudioAttributesCompat);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setAudioSessionId(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setAuxEffectSendLevel(@FloatRange(from=0, to=1) float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setMediaItem(androidx.media2.common.MediaItem);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaybackParams(androidx.media2.player.PlaybackParams);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaybackSpeed(@FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE, fromInclusive=false) float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlayerVolume(@FloatRange(from=0, to=1) float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaylist(java.util.List<androidx.media2.common.MediaItem!>, androidx.media2.common.MediaMetadata?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setRepeatMode(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setShuffleMode(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToNextPlaylistItem();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToPlaylistItem(@IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToPreviousPlaylistItem();
+    method public void unregisterPlayerCallback(androidx.media2.player.MediaPlayer.PlayerCallback);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> updatePlaylistMetadata(androidx.media2.common.MediaMetadata?);
+    field public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804; // 0x324
+    field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320
+    field public static final int MEDIA_INFO_BUFFERING_UPDATE = 704; // 0x2c0
+    field public static final int MEDIA_INFO_METADATA_UPDATE = 802; // 0x322
+    field public static final int MEDIA_INFO_NOT_SEEKABLE = 801; // 0x321
+    field public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; // 0x325
+    field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
+    field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
+    field @Deprecated public static final int NO_TRACK_SELECTED = -2147483648; // 0x80000000
+    field public static final int PLAYER_ERROR_IO = -1004; // 0xfffffc14
+    field public static final int PLAYER_ERROR_MALFORMED = -1007; // 0xfffffc11
+    field public static final int PLAYER_ERROR_TIMED_OUT = -110; // 0xffffff92
+    field public static final int PLAYER_ERROR_UNKNOWN = 1; // 0x1
+    field public static final int PLAYER_ERROR_UNSUPPORTED = -1010; // 0xfffffc0e
+    field public static final int SEEK_CLOSEST = 3; // 0x3
+    field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
+    field public static final int SEEK_NEXT_SYNC = 1; // 0x1
+    field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0
+  }
+
+  public abstract static class MediaPlayer.PlayerCallback extends androidx.media2.common.SessionPlayer.PlayerCallback {
+    ctor public MediaPlayer.PlayerCallback();
+    method public void onError(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, int, int);
+    method public void onInfo(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, int, int);
+    method public void onMediaTimeDiscontinuity(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, androidx.media2.player.MediaTimestamp);
+    method public void onTimedMetaDataAvailable(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, androidx.media2.player.TimedMetaData);
+    method @Deprecated public void onVideoSizeChanged(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, androidx.media2.player.VideoSize);
+  }
+
+  public static final class MediaPlayer.TrackInfo extends androidx.media2.common.SessionPlayer.TrackInfo {
+  }
+
+  public final class MediaTimestamp {
+    method public long getAnchorMediaTimeUs();
+    method public long getAnchorSystemNanoTime();
+    method public float getMediaClockRate();
+    field public static final androidx.media2.player.MediaTimestamp TIMESTAMP_UNKNOWN;
+  }
+
+  public final class PlaybackParams {
+    method public Integer? getAudioFallbackMode();
+    method public Float? getPitch();
+    method public Float? getSpeed();
+    field public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0; // 0x0
+    field public static final int AUDIO_FALLBACK_MODE_FAIL = 2; // 0x2
+    field public static final int AUDIO_FALLBACK_MODE_MUTE = 1; // 0x1
+  }
+
+  public static final class PlaybackParams.Builder {
+    ctor public PlaybackParams.Builder();
+    ctor public PlaybackParams.Builder(androidx.media2.player.PlaybackParams);
+    method public androidx.media2.player.PlaybackParams build();
+    method public androidx.media2.player.PlaybackParams.Builder setAudioFallbackMode(int);
+    method public androidx.media2.player.PlaybackParams.Builder setPitch(@FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE, fromInclusive=false) float);
+    method public androidx.media2.player.PlaybackParams.Builder setSpeed(@FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE, fromInclusive=false) float);
+  }
+
+  public class TimedMetaData {
+    method public byte[]! getMetaData();
+    method public long getTimestamp();
+  }
+
+  public final class VideoSize extends androidx.media2.common.VideoSize {
+    ctor public VideoSize(int, int);
+  }
+
+}
+
diff --git a/media2/player/api/res-1.1.0-beta01.txt b/media2/player/api/res-1.1.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media2/player/api/res-1.1.0-beta01.txt
diff --git a/media2/player/api/restricted_1.1.0-beta01.txt b/media2/player/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..8a75ae5
--- /dev/null
+++ b/media2/player/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,123 @@
+// Signature format: 3.0
+package androidx.media2.player {
+
+  public final class MediaPlayer extends androidx.media2.common.SessionPlayer {
+    ctor public MediaPlayer(android.content.Context);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> addPlaylistItem(int, androidx.media2.common.MediaItem);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> attachAuxEffect(int);
+    method public androidx.media.AudioAttributesCompat? getAudioAttributes();
+    method public int getAudioSessionId();
+    method public long getBufferedPosition();
+    method @androidx.media2.common.SessionPlayer.BuffState public int getBufferingState();
+    method public androidx.media2.common.MediaItem? getCurrentMediaItem();
+    method public int getCurrentMediaItemIndex();
+    method public long getCurrentPosition();
+    method public long getDuration();
+    method public float getMaxPlayerVolume();
+    method public int getNextMediaItemIndex();
+    method public androidx.media2.player.PlaybackParams getPlaybackParams();
+    method @FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE, fromInclusive=false) public float getPlaybackSpeed();
+    method @androidx.media2.common.SessionPlayer.PlayerState public int getPlayerState();
+    method public float getPlayerVolume();
+    method public java.util.List<androidx.media2.common.MediaItem!>? getPlaylist();
+    method public androidx.media2.common.MediaMetadata? getPlaylistMetadata();
+    method public int getPreviousMediaItemIndex();
+    method public int getRepeatMode();
+    method public androidx.media2.player.MediaPlayer.TrackInfo? getSelectedTrack(int);
+    method public int getShuffleMode();
+    method public androidx.media2.player.MediaTimestamp? getTimestamp();
+    method @Deprecated public java.util.List<androidx.media2.player.MediaPlayer.TrackInfo!> getTrackInfo();
+    method public androidx.media2.player.VideoSize getVideoSize();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> pause();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> play();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> prepare();
+    method public void registerPlayerCallback(java.util.concurrent.Executor, androidx.media2.player.MediaPlayer.PlayerCallback);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> removePlaylistItem(@IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> replacePlaylistItem(int, androidx.media2.common.MediaItem);
+    method public void reset();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> seekTo(long);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> seekTo(long, int);
+    method @Deprecated public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> selectTrack(androidx.media2.player.MediaPlayer.TrackInfo);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setAudioAttributes(androidx.media.AudioAttributesCompat);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setAudioSessionId(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setAuxEffectSendLevel(@FloatRange(from=0, to=1) float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setMediaItem(androidx.media2.common.MediaItem);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaybackParams(androidx.media2.player.PlaybackParams);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaybackSpeed(@FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE, fromInclusive=false) float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlayerVolume(@FloatRange(from=0, to=1) float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setPlaylist(java.util.List<androidx.media2.common.MediaItem!>, androidx.media2.common.MediaMetadata?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setRepeatMode(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> setShuffleMode(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToNextPlaylistItem();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToPlaylistItem(@IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> skipToPreviousPlaylistItem();
+    method public void unregisterPlayerCallback(androidx.media2.player.MediaPlayer.PlayerCallback);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.common.SessionPlayer.PlayerResult!> updatePlaylistMetadata(androidx.media2.common.MediaMetadata?);
+    field public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804; // 0x324
+    field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320
+    field public static final int MEDIA_INFO_BUFFERING_UPDATE = 704; // 0x2c0
+    field public static final int MEDIA_INFO_METADATA_UPDATE = 802; // 0x322
+    field public static final int MEDIA_INFO_NOT_SEEKABLE = 801; // 0x321
+    field public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; // 0x325
+    field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
+    field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
+    field @Deprecated public static final int NO_TRACK_SELECTED = -2147483648; // 0x80000000
+    field public static final int PLAYER_ERROR_IO = -1004; // 0xfffffc14
+    field public static final int PLAYER_ERROR_MALFORMED = -1007; // 0xfffffc11
+    field public static final int PLAYER_ERROR_TIMED_OUT = -110; // 0xffffff92
+    field public static final int PLAYER_ERROR_UNKNOWN = 1; // 0x1
+    field public static final int PLAYER_ERROR_UNSUPPORTED = -1010; // 0xfffffc0e
+    field public static final int SEEK_CLOSEST = 3; // 0x3
+    field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
+    field public static final int SEEK_NEXT_SYNC = 1; // 0x1
+    field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0
+  }
+
+  public abstract static class MediaPlayer.PlayerCallback extends androidx.media2.common.SessionPlayer.PlayerCallback {
+    ctor public MediaPlayer.PlayerCallback();
+    method public void onError(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, int, int);
+    method public void onInfo(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, int, int);
+    method public void onMediaTimeDiscontinuity(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, androidx.media2.player.MediaTimestamp);
+    method public void onTimedMetaDataAvailable(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, androidx.media2.player.TimedMetaData);
+    method @Deprecated public void onVideoSizeChanged(androidx.media2.player.MediaPlayer, androidx.media2.common.MediaItem, androidx.media2.player.VideoSize);
+  }
+
+  public static final class MediaPlayer.TrackInfo extends androidx.media2.common.SessionPlayer.TrackInfo {
+  }
+
+  public final class MediaTimestamp {
+    method public long getAnchorMediaTimeUs();
+    method public long getAnchorSystemNanoTime();
+    method public float getMediaClockRate();
+    field public static final androidx.media2.player.MediaTimestamp TIMESTAMP_UNKNOWN;
+  }
+
+  public final class PlaybackParams {
+    method public Integer? getAudioFallbackMode();
+    method public Float? getPitch();
+    method public Float? getSpeed();
+    field public static final int AUDIO_FALLBACK_MODE_DEFAULT = 0; // 0x0
+    field public static final int AUDIO_FALLBACK_MODE_FAIL = 2; // 0x2
+    field public static final int AUDIO_FALLBACK_MODE_MUTE = 1; // 0x1
+  }
+
+  public static final class PlaybackParams.Builder {
+    ctor public PlaybackParams.Builder();
+    ctor public PlaybackParams.Builder(androidx.media2.player.PlaybackParams);
+    method public androidx.media2.player.PlaybackParams build();
+    method public androidx.media2.player.PlaybackParams.Builder setAudioFallbackMode(int);
+    method public androidx.media2.player.PlaybackParams.Builder setPitch(@FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE, fromInclusive=false) float);
+    method public androidx.media2.player.PlaybackParams.Builder setSpeed(@FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE, fromInclusive=false) float);
+  }
+
+  public class TimedMetaData {
+    method public byte[]! getMetaData();
+    method public long getTimestamp();
+  }
+
+  public final class VideoSize extends androidx.media2.common.VideoSize {
+    ctor public VideoSize(int, int);
+  }
+
+}
+
diff --git a/media2/session/api/1.1.0-beta01.txt b/media2/session/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..d5c19ab
--- /dev/null
+++ b/media2/session/api/1.1.0-beta01.txt
@@ -0,0 +1,447 @@
+// Signature format: 3.0
+package androidx.media2.session {
+
+  public final class HeartRating implements androidx.media2.common.Rating {
+    ctor public HeartRating();
+    ctor public HeartRating(boolean);
+    method public boolean hasHeart();
+    method public boolean isRated();
+  }
+
+  public class LibraryResult implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public LibraryResult(int);
+    ctor public LibraryResult(int, androidx.media2.common.MediaItem?, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    ctor public LibraryResult(int, java.util.List<androidx.media2.common.MediaItem!>?, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public long getCompletionTime();
+    method public androidx.media2.session.MediaLibraryService.LibraryParams? getLibraryParams();
+    method public androidx.media2.common.MediaItem? getMediaItem();
+    method public java.util.List<androidx.media2.common.MediaItem!>? getMediaItems();
+    method public int getResultCode();
+    field public static final int RESULT_ERROR_BAD_VALUE = -3; // 0xfffffffd
+    field public static final int RESULT_ERROR_INVALID_STATE = -2; // 0xfffffffe
+    field public static final int RESULT_ERROR_IO = -5; // 0xfffffffb
+    field public static final int RESULT_ERROR_NOT_SUPPORTED = -6; // 0xfffffffa
+    field public static final int RESULT_ERROR_PERMISSION_DENIED = -4; // 0xfffffffc
+    field public static final int RESULT_ERROR_SESSION_AUTHENTICATION_EXPIRED = -102; // 0xffffff9a
+    field public static final int RESULT_ERROR_SESSION_CONCURRENT_STREAM_LIMIT = -104; // 0xffffff98
+    field public static final int RESULT_ERROR_SESSION_DISCONNECTED = -100; // 0xffffff9c
+    field public static final int RESULT_ERROR_SESSION_NOT_AVAILABLE_IN_REGION = -106; // 0xffffff96
+    field public static final int RESULT_ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED = -105; // 0xffffff97
+    field public static final int RESULT_ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED = -103; // 0xffffff99
+    field public static final int RESULT_ERROR_SESSION_SETUP_REQUIRED = -108; // 0xffffff94
+    field public static final int RESULT_ERROR_SESSION_SKIP_LIMIT_REACHED = -107; // 0xffffff95
+    field public static final int RESULT_ERROR_UNKNOWN = -1; // 0xffffffff
+    field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  public class MediaBrowser extends androidx.media2.session.MediaController {
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> getChildren(String, @IntRange(from=0) int, @IntRange(from=1) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> getItem(String);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> getLibraryRoot(androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> getSearchResult(String, @IntRange(from=0) int, @IntRange(from=1) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> search(String, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> subscribe(String, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> unsubscribe(String);
+  }
+
+  public static class MediaBrowser.BrowserCallback extends androidx.media2.session.MediaController.ControllerCallback {
+    ctor public MediaBrowser.BrowserCallback();
+    method public void onChildrenChanged(androidx.media2.session.MediaBrowser, String, @IntRange(from=0) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public void onSearchResultChanged(androidx.media2.session.MediaBrowser, String, @IntRange(from=0) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+  }
+
+  public static final class MediaBrowser.Builder {
+    ctor public MediaBrowser.Builder(android.content.Context);
+    method public androidx.media2.session.MediaBrowser build();
+    method public androidx.media2.session.MediaBrowser.Builder setConnectionHints(android.os.Bundle);
+    method public androidx.media2.session.MediaBrowser.Builder setControllerCallback(java.util.concurrent.Executor, androidx.media2.session.MediaBrowser.BrowserCallback);
+    method public androidx.media2.session.MediaBrowser.Builder setSessionCompatToken(android.support.v4.media.session.MediaSessionCompat.Token);
+    method public androidx.media2.session.MediaBrowser.Builder setSessionToken(androidx.media2.session.SessionToken);
+  }
+
+  public class MediaConstants {
+    field public static final String MEDIA_URI_AUTHORITY = "media2-session";
+    field public static final String MEDIA_URI_PATH_PLAY_FROM_MEDIA_ID = "playFromMediaId";
+    field public static final String MEDIA_URI_PATH_PLAY_FROM_SEARCH = "playFromSearch";
+    field public static final String MEDIA_URI_PATH_PREPARE_FROM_MEDIA_ID = "prepareFromMediaId";
+    field public static final String MEDIA_URI_PATH_PREPARE_FROM_SEARCH = "prepareFromSearch";
+    field public static final String MEDIA_URI_QUERY_ID = "id";
+    field public static final String MEDIA_URI_QUERY_QUERY = "query";
+    field public static final String MEDIA_URI_SCHEME = "androidx";
+  }
+
+  public class MediaController implements java.io.Closeable {
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> addPlaylistItem(@IntRange(from=0) int, String);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> adjustVolume(int, int);
+    method public void close();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> deselectTrack(androidx.media2.common.SessionPlayer.TrackInfo);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> fastForward();
+    method public androidx.media2.session.SessionCommandGroup? getAllowedCommands();
+    method public long getBufferedPosition();
+    method public int getBufferingState();
+    method public androidx.media2.session.SessionToken? getConnectedToken();
+    method public androidx.media2.common.MediaItem? getCurrentMediaItem();
+    method public int getCurrentMediaItemIndex();
+    method public long getCurrentPosition();
+    method public long getDuration();
+    method public int getNextMediaItemIndex();
+    method public androidx.media2.session.MediaController.PlaybackInfo? getPlaybackInfo();
+    method public float getPlaybackSpeed();
+    method public int getPlayerState();
+    method public java.util.List<androidx.media2.common.MediaItem!>? getPlaylist();
+    method public androidx.media2.common.MediaMetadata? getPlaylistMetadata();
+    method public int getPreviousMediaItemIndex();
+    method public int getRepeatMode();
+    method public androidx.media2.common.SessionPlayer.TrackInfo? getSelectedTrack(int);
+    method public android.app.PendingIntent? getSessionActivity();
+    method public int getShuffleMode();
+    method public java.util.List<androidx.media2.common.SessionPlayer.TrackInfo!> getTracks();
+    method public androidx.media2.common.VideoSize getVideoSize();
+    method public boolean isConnected();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> movePlaylistItem(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> pause();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> play();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> prepare();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> removePlaylistItem(@IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> replacePlaylistItem(@IntRange(from=0) int, String);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> rewind();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> seekTo(long);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> selectTrack(androidx.media2.common.SessionPlayer.TrackInfo);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> sendCustomCommand(androidx.media2.session.SessionCommand, android.os.Bundle?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setMediaItem(String);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setMediaUri(android.net.Uri, android.os.Bundle?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setPlaybackSpeed(float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setPlaylist(java.util.List<java.lang.String!>, androidx.media2.common.MediaMetadata?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setRating(String, androidx.media2.common.Rating);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setRepeatMode(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setShuffleMode(int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setSurface(android.view.Surface?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setVolumeTo(int, int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipBackward();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipForward();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipToNextPlaylistItem();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipToPlaylistItem(@IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipToPreviousPlaylistItem();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> updatePlaylistMetadata(androidx.media2.common.MediaMetadata?);
+  }
+
+  public static final class MediaController.Builder {
+    ctor public MediaController.Builder(android.content.Context);
+    method public androidx.media2.session.MediaController build();
+    method public androidx.media2.session.MediaController.Builder setConnectionHints(android.os.Bundle);
+    method public androidx.media2.session.MediaController.Builder setControllerCallback(java.util.concurrent.Executor, androidx.media2.session.MediaController.ControllerCallback);
+    method public androidx.media2.session.MediaController.Builder setSessionCompatToken(android.support.v4.media.session.MediaSessionCompat.Token);
+    method public androidx.media2.session.MediaController.Builder setSessionToken(androidx.media2.session.SessionToken);
+  }
+
+  public abstract static class MediaController.ControllerCallback {
+    ctor public MediaController.ControllerCallback();
+    method public void onAllowedCommandsChanged(androidx.media2.session.MediaController, androidx.media2.session.SessionCommandGroup);
+    method public void onBufferingStateChanged(androidx.media2.session.MediaController, androidx.media2.common.MediaItem, int);
+    method public void onConnected(androidx.media2.session.MediaController, androidx.media2.session.SessionCommandGroup);
+    method public void onCurrentMediaItemChanged(androidx.media2.session.MediaController, androidx.media2.common.MediaItem?);
+    method public androidx.media2.session.SessionResult onCustomCommand(androidx.media2.session.MediaController, androidx.media2.session.SessionCommand, android.os.Bundle?);
+    method public void onDisconnected(androidx.media2.session.MediaController);
+    method public void onPlaybackCompleted(androidx.media2.session.MediaController);
+    method public void onPlaybackInfoChanged(androidx.media2.session.MediaController, androidx.media2.session.MediaController.PlaybackInfo);
+    method public void onPlaybackSpeedChanged(androidx.media2.session.MediaController, float);
+    method public void onPlayerStateChanged(androidx.media2.session.MediaController, int);
+    method public void onPlaylistChanged(androidx.media2.session.MediaController, java.util.List<androidx.media2.common.MediaItem!>?, androidx.media2.common.MediaMetadata?);
+    method public void onPlaylistMetadataChanged(androidx.media2.session.MediaController, androidx.media2.common.MediaMetadata?);
+    method public void onRepeatModeChanged(androidx.media2.session.MediaController, int);
+    method public void onSeekCompleted(androidx.media2.session.MediaController, long);
+    method public int onSetCustomLayout(androidx.media2.session.MediaController, java.util.List<androidx.media2.session.MediaSession.CommandButton!>);
+    method public void onShuffleModeChanged(androidx.media2.session.MediaController, int);
+    method public void onSubtitleData(androidx.media2.session.MediaController, androidx.media2.common.MediaItem, androidx.media2.common.SessionPlayer.TrackInfo, androidx.media2.common.SubtitleData);
+    method public void onTrackDeselected(androidx.media2.session.MediaController, androidx.media2.common.SessionPlayer.TrackInfo);
+    method public void onTrackSelected(androidx.media2.session.MediaController, androidx.media2.common.SessionPlayer.TrackInfo);
+    method public void onTracksChanged(androidx.media2.session.MediaController, java.util.List<androidx.media2.common.SessionPlayer.TrackInfo!>);
+    method public void onVideoSizeChanged(androidx.media2.session.MediaController, androidx.media2.common.VideoSize);
+  }
+
+  public static final class MediaController.PlaybackInfo implements androidx.versionedparcelable.VersionedParcelable {
+    method public androidx.media.AudioAttributesCompat? getAudioAttributes();
+    method public int getControlType();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getPlaybackType();
+    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
+  }
+
+  public abstract class MediaLibraryService extends androidx.media2.session.MediaSessionService {
+    ctor public MediaLibraryService();
+    method public abstract androidx.media2.session.MediaLibraryService.MediaLibrarySession? onGetSession(androidx.media2.session.MediaSession.ControllerInfo);
+    field public static final String SERVICE_INTERFACE = "androidx.media2.session.MediaLibraryService";
+  }
+
+  public static final class MediaLibraryService.LibraryParams implements androidx.versionedparcelable.VersionedParcelable {
+    method public android.os.Bundle? getExtras();
+    method public boolean isOffline();
+    method public boolean isRecent();
+    method public boolean isSuggested();
+  }
+
+  public static final class MediaLibraryService.LibraryParams.Builder {
+    ctor public MediaLibraryService.LibraryParams.Builder();
+    method public androidx.media2.session.MediaLibraryService.LibraryParams build();
+    method public androidx.media2.session.MediaLibraryService.LibraryParams.Builder setExtras(android.os.Bundle?);
+    method public androidx.media2.session.MediaLibraryService.LibraryParams.Builder setOffline(boolean);
+    method public androidx.media2.session.MediaLibraryService.LibraryParams.Builder setRecent(boolean);
+    method public androidx.media2.session.MediaLibraryService.LibraryParams.Builder setSuggested(boolean);
+  }
+
+  public static final class MediaLibraryService.MediaLibrarySession extends androidx.media2.session.MediaSession {
+    method public void notifyChildrenChanged(androidx.media2.session.MediaSession.ControllerInfo, String, @IntRange(from=0) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public void notifyChildrenChanged(String, int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public void notifySearchResultChanged(androidx.media2.session.MediaSession.ControllerInfo, String, @IntRange(from=0) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+  }
+
+  public static final class MediaLibraryService.MediaLibrarySession.Builder {
+    ctor public MediaLibraryService.MediaLibrarySession.Builder(androidx.media2.session.MediaLibraryService, androidx.media2.common.SessionPlayer, java.util.concurrent.Executor, androidx.media2.session.MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback);
+    method public androidx.media2.session.MediaLibraryService.MediaLibrarySession build();
+    method public androidx.media2.session.MediaLibraryService.MediaLibrarySession.Builder setExtras(android.os.Bundle);
+    method public androidx.media2.session.MediaLibraryService.MediaLibrarySession.Builder setId(String);
+    method public androidx.media2.session.MediaLibraryService.MediaLibrarySession.Builder setSessionActivity(android.app.PendingIntent?);
+  }
+
+  public static class MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback extends androidx.media2.session.MediaSession.SessionCallback {
+    ctor public MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback();
+    method public androidx.media2.session.LibraryResult onGetChildren(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String, @IntRange(from=0) int, @IntRange(from=1) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public androidx.media2.session.LibraryResult onGetItem(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String);
+    method public androidx.media2.session.LibraryResult onGetLibraryRoot(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public androidx.media2.session.LibraryResult onGetSearchResult(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String, @IntRange(from=0) int, @IntRange(from=1) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public int onSearch(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public int onSubscribe(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public int onUnsubscribe(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String);
+  }
+
+  public class MediaSession implements java.io.Closeable {
+    method public void broadcastCustomCommand(androidx.media2.session.SessionCommand, android.os.Bundle?);
+    method public void close();
+    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.Token getSessionCompatToken();
+    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);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setCustomLayout(androidx.media2.session.MediaSession.ControllerInfo, java.util.List<androidx.media2.session.MediaSession.CommandButton!>);
+    method public void updatePlayer(androidx.media2.common.SessionPlayer);
+  }
+
+  public static final class MediaSession.Builder {
+    ctor public MediaSession.Builder(android.content.Context, androidx.media2.common.SessionPlayer);
+    method public androidx.media2.session.MediaSession build();
+    method public androidx.media2.session.MediaSession.Builder setExtras(android.os.Bundle);
+    method public androidx.media2.session.MediaSession.Builder setId(String);
+    method public androidx.media2.session.MediaSession.Builder setSessionActivity(android.app.PendingIntent?);
+    method public androidx.media2.session.MediaSession.Builder setSessionCallback(java.util.concurrent.Executor, androidx.media2.session.MediaSession.SessionCallback);
+  }
+
+  public static final class MediaSession.CommandButton implements androidx.versionedparcelable.VersionedParcelable {
+    method public androidx.media2.session.SessionCommand? getCommand();
+    method public CharSequence? getDisplayName();
+    method public android.os.Bundle? getExtras();
+    method public int getIconResId();
+    method public boolean isEnabled();
+  }
+
+  public static final class MediaSession.CommandButton.Builder {
+    ctor public MediaSession.CommandButton.Builder();
+    method public androidx.media2.session.MediaSession.CommandButton build();
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setCommand(androidx.media2.session.SessionCommand?);
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setDisplayName(CharSequence?);
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setEnabled(boolean);
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setExtras(android.os.Bundle?);
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setIconResId(int);
+  }
+
+  public static final class MediaSession.ControllerInfo {
+    method public android.os.Bundle getConnectionHints();
+    method public String getPackageName();
+    method public int getUid();
+  }
+
+  public abstract static class MediaSession.SessionCallback {
+    ctor public MediaSession.SessionCallback();
+    method public int onCommandRequest(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, androidx.media2.session.SessionCommand);
+    method public androidx.media2.session.SessionCommandGroup? onConnect(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public androidx.media2.common.MediaItem? onCreateMediaItem(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, String);
+    method public androidx.media2.session.SessionResult onCustomCommand(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, androidx.media2.session.SessionCommand, android.os.Bundle?);
+    method public void onDisconnected(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public int onFastForward(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public void onPostConnect(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public int onRewind(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public int onSetMediaUri(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, android.net.Uri, android.os.Bundle?);
+    method public int onSetRating(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, String, androidx.media2.common.Rating);
+    method public int onSkipBackward(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public int onSkipForward(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+  }
+
+  public final class MediaSessionManager {
+    method public static androidx.media2.session.MediaSessionManager getInstance(android.content.Context);
+    method public java.util.Set<androidx.media2.session.SessionToken!> getSessionServiceTokens();
+  }
+
+  public abstract class MediaSessionService extends android.app.Service {
+    ctor public MediaSessionService();
+    method public final void addSession(androidx.media2.session.MediaSession);
+    method public final java.util.List<androidx.media2.session.MediaSession!> getSessions();
+    method @CallSuper public android.os.IBinder? onBind(android.content.Intent);
+    method public abstract androidx.media2.session.MediaSession? onGetSession(androidx.media2.session.MediaSession.ControllerInfo);
+    method public androidx.media2.session.MediaSessionService.MediaNotification? onUpdateNotification(androidx.media2.session.MediaSession);
+    method public final void removeSession(androidx.media2.session.MediaSession);
+    field public static final String SERVICE_INTERFACE = "androidx.media2.session.MediaSessionService";
+  }
+
+  public static class MediaSessionService.MediaNotification {
+    ctor public MediaSessionService.MediaNotification(int, android.app.Notification);
+    method public android.app.Notification getNotification();
+    method public int getNotificationId();
+  }
+
+  public final class PercentageRating implements androidx.media2.common.Rating {
+    ctor public PercentageRating();
+    ctor public PercentageRating(float);
+    method public float getPercentRating();
+    method public boolean isRated();
+  }
+
+  public abstract class RemoteSessionPlayer extends androidx.media2.common.SessionPlayer {
+    ctor public RemoteSessionPlayer();
+    method public abstract java.util.concurrent.Future<androidx.media2.common.SessionPlayer.PlayerResult!> adjustVolume(int);
+    method public abstract int getMaxVolume();
+    method public abstract int getVolume();
+    method public abstract int getVolumeControlType();
+    method public abstract java.util.concurrent.Future<androidx.media2.common.SessionPlayer.PlayerResult!> setVolume(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
+  public static class RemoteSessionPlayer.Callback extends androidx.media2.common.SessionPlayer.PlayerCallback {
+    ctor public RemoteSessionPlayer.Callback();
+    method public void onVolumeChanged(androidx.media2.session.RemoteSessionPlayer, int);
+  }
+
+  public final class SessionCommand implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SessionCommand(int);
+    ctor public SessionCommand(String, android.os.Bundle?);
+    method public int getCommandCode();
+    method public String? getCustomAction();
+    method public android.os.Bundle? getCustomExtras();
+    field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
+    field public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 50003; // 0xc353
+    field public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 50004; // 0xc354
+    field public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 50000; // 0xc350
+    field public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 50006; // 0xc356
+    field public static final int COMMAND_CODE_LIBRARY_SEARCH = 50005; // 0xc355
+    field public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 50001; // 0xc351
+    field public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 50002; // 0xc352
+    field public static final int COMMAND_CODE_PLAYER_ADD_PLAYLIST_ITEM = 10013; // 0x271d
+    field public static final int COMMAND_CODE_PLAYER_DESELECT_TRACK = 11002; // 0x2afa
+    field public static final int COMMAND_CODE_PLAYER_GET_CURRENT_MEDIA_ITEM = 10016; // 0x2720
+    field public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST = 10005; // 0x2715
+    field public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST_METADATA = 10012; // 0x271c
+    field public static final int COMMAND_CODE_PLAYER_MOVE_PLAYLIST_ITEM = 10019; // 0x2723
+    field public static final int COMMAND_CODE_PLAYER_PAUSE = 10001; // 0x2711
+    field public static final int COMMAND_CODE_PLAYER_PLAY = 10000; // 0x2710
+    field public static final int COMMAND_CODE_PLAYER_PREPARE = 10002; // 0x2712
+    field public static final int COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM = 10014; // 0x271e
+    field public static final int COMMAND_CODE_PLAYER_REPLACE_PLAYLIST_ITEM = 10015; // 0x271f
+    field public static final int COMMAND_CODE_PLAYER_SEEK_TO = 10003; // 0x2713
+    field public static final int COMMAND_CODE_PLAYER_SELECT_TRACK = 11001; // 0x2af9
+    field public static final int COMMAND_CODE_PLAYER_SET_MEDIA_ITEM = 10018; // 0x2722
+    field public static final int COMMAND_CODE_PLAYER_SET_PLAYLIST = 10006; // 0x2716
+    field public static final int COMMAND_CODE_PLAYER_SET_REPEAT_MODE = 10011; // 0x271b
+    field public static final int COMMAND_CODE_PLAYER_SET_SHUFFLE_MODE = 10010; // 0x271a
+    field public static final int COMMAND_CODE_PLAYER_SET_SPEED = 10004; // 0x2714
+    field public static final int COMMAND_CODE_PLAYER_SET_SURFACE = 11000; // 0x2af8
+    field public static final int COMMAND_CODE_PLAYER_SKIP_TO_NEXT_PLAYLIST_ITEM = 10009; // 0x2719
+    field public static final int COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM = 10007; // 0x2717
+    field public static final int COMMAND_CODE_PLAYER_SKIP_TO_PREVIOUS_PLAYLIST_ITEM = 10008; // 0x2718
+    field public static final int COMMAND_CODE_PLAYER_UPDATE_LIST_METADATA = 10017; // 0x2721
+    field public static final int COMMAND_CODE_SESSION_FAST_FORWARD = 40000; // 0x9c40
+    field public static final int COMMAND_CODE_SESSION_REWIND = 40001; // 0x9c41
+    field public static final int COMMAND_CODE_SESSION_SET_MEDIA_URI = 40011; // 0x9c4b
+    field public static final int COMMAND_CODE_SESSION_SET_RATING = 40010; // 0x9c4a
+    field public static final int COMMAND_CODE_SESSION_SKIP_BACKWARD = 40003; // 0x9c43
+    field public static final int COMMAND_CODE_SESSION_SKIP_FORWARD = 40002; // 0x9c42
+    field public static final int COMMAND_CODE_VOLUME_ADJUST_VOLUME = 30001; // 0x7531
+    field public static final int COMMAND_CODE_VOLUME_SET_VOLUME = 30000; // 0x7530
+    field public static final int COMMAND_VERSION_1 = 1; // 0x1
+    field public static final int COMMAND_VERSION_2 = 2; // 0x2
+  }
+
+  public final class SessionCommandGroup implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SessionCommandGroup();
+    ctor public SessionCommandGroup(java.util.Collection<androidx.media2.session.SessionCommand!>?);
+    method public java.util.Set<androidx.media2.session.SessionCommand!> getCommands();
+    method public boolean hasCommand(androidx.media2.session.SessionCommand);
+    method public boolean hasCommand(int);
+  }
+
+  public static final class SessionCommandGroup.Builder {
+    ctor public SessionCommandGroup.Builder();
+    ctor public SessionCommandGroup.Builder(androidx.media2.session.SessionCommandGroup);
+    method public androidx.media2.session.SessionCommandGroup.Builder addAllPredefinedCommands(int);
+    method public androidx.media2.session.SessionCommandGroup.Builder addCommand(androidx.media2.session.SessionCommand);
+    method public androidx.media2.session.SessionCommandGroup build();
+    method public androidx.media2.session.SessionCommandGroup.Builder removeCommand(androidx.media2.session.SessionCommand);
+  }
+
+  public class SessionResult implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SessionResult(int, android.os.Bundle?);
+    method public long getCompletionTime();
+    method public android.os.Bundle? getCustomCommandResult();
+    method public androidx.media2.common.MediaItem? getMediaItem();
+    method public int getResultCode();
+    field public static final int RESULT_ERROR_BAD_VALUE = -3; // 0xfffffffd
+    field public static final int RESULT_ERROR_INVALID_STATE = -2; // 0xfffffffe
+    field public static final int RESULT_ERROR_IO = -5; // 0xfffffffb
+    field public static final int RESULT_ERROR_NOT_SUPPORTED = -6; // 0xfffffffa
+    field public static final int RESULT_ERROR_PERMISSION_DENIED = -4; // 0xfffffffc
+    field public static final int RESULT_ERROR_SESSION_AUTHENTICATION_EXPIRED = -102; // 0xffffff9a
+    field public static final int RESULT_ERROR_SESSION_CONCURRENT_STREAM_LIMIT = -104; // 0xffffff98
+    field public static final int RESULT_ERROR_SESSION_DISCONNECTED = -100; // 0xffffff9c
+    field public static final int RESULT_ERROR_SESSION_NOT_AVAILABLE_IN_REGION = -106; // 0xffffff96
+    field public static final int RESULT_ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED = -105; // 0xffffff97
+    field public static final int RESULT_ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED = -103; // 0xffffff99
+    field public static final int RESULT_ERROR_SESSION_SETUP_REQUIRED = -108; // 0xffffff94
+    field public static final int RESULT_ERROR_SESSION_SKIP_LIMIT_REACHED = -107; // 0xffffff95
+    field public static final int RESULT_ERROR_UNKNOWN = -1; // 0xffffffff
+    field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  public final class SessionToken implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SessionToken(android.content.Context, android.content.ComponentName);
+    method public android.os.Bundle getExtras();
+    method public String getPackageName();
+    method public String? getServiceName();
+    method public int getType();
+    method public int getUid();
+    field public static final int TYPE_LIBRARY_SERVICE = 2; // 0x2
+    field public static final int TYPE_SESSION = 0; // 0x0
+    field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
+  }
+
+  public final class StarRating implements androidx.media2.common.Rating {
+    ctor public StarRating(@IntRange(from=1) int);
+    ctor public StarRating(@IntRange(from=1) int, float);
+    method public int getMaxStars();
+    method public float getStarRating();
+    method public boolean isRated();
+  }
+
+  public final class ThumbRating implements androidx.media2.common.Rating {
+    ctor public ThumbRating();
+    ctor public ThumbRating(boolean);
+    method public boolean isRated();
+    method public boolean isThumbUp();
+  }
+
+}
+
diff --git a/media2/session/api/public_plus_experimental_1.1.0-beta01.txt b/media2/session/api/public_plus_experimental_1.1.0-beta01.txt
new file mode 100644
index 0000000..646d255
--- /dev/null
+++ b/media2/session/api/public_plus_experimental_1.1.0-beta01.txt
@@ -0,0 +1,432 @@
+// Signature format: 3.0
+package androidx.media2.session {
+
+  @androidx.versionedparcelable.VersionedParcelize public final class HeartRating implements androidx.media2.common.Rating {
+    ctor public HeartRating();
+    ctor public HeartRating(boolean);
+    method public boolean hasHeart();
+    method public boolean isRated();
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(isCustom=true) public class LibraryResult extends androidx.versionedparcelable.CustomVersionedParcelable implements androidx.media2.common.BaseResult {
+    ctor public LibraryResult(int);
+    ctor public LibraryResult(int, androidx.media2.common.MediaItem?, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    ctor public LibraryResult(int, java.util.List<androidx.media2.common.MediaItem!>?, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public long getCompletionTime();
+    method public androidx.media2.session.MediaLibraryService.LibraryParams? getLibraryParams();
+    method public androidx.media2.common.MediaItem? getMediaItem();
+    method public java.util.List<androidx.media2.common.MediaItem!>? getMediaItems();
+    method public int getResultCode();
+    field public static final int RESULT_ERROR_SESSION_AUTHENTICATION_EXPIRED = -102; // 0xffffff9a
+    field public static final int RESULT_ERROR_SESSION_CONCURRENT_STREAM_LIMIT = -104; // 0xffffff98
+    field public static final int RESULT_ERROR_SESSION_DISCONNECTED = -100; // 0xffffff9c
+    field public static final int RESULT_ERROR_SESSION_NOT_AVAILABLE_IN_REGION = -106; // 0xffffff96
+    field public static final int RESULT_ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED = -105; // 0xffffff97
+    field public static final int RESULT_ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED = -103; // 0xffffff99
+    field public static final int RESULT_ERROR_SESSION_SETUP_REQUIRED = -108; // 0xffffff94
+    field public static final int RESULT_ERROR_SESSION_SKIP_LIMIT_REACHED = -107; // 0xffffff95
+  }
+
+  public class MediaBrowser extends androidx.media2.session.MediaController {
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> getChildren(String, @IntRange(from=0) int, @IntRange(from=1) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> getItem(String);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> getLibraryRoot(androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> getSearchResult(String, @IntRange(from=0) int, @IntRange(from=1) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> search(String, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> subscribe(String, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> unsubscribe(String);
+  }
+
+  public static class MediaBrowser.BrowserCallback extends androidx.media2.session.MediaController.ControllerCallback {
+    ctor public MediaBrowser.BrowserCallback();
+    method public void onChildrenChanged(androidx.media2.session.MediaBrowser, String, @IntRange(from=0) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public void onSearchResultChanged(androidx.media2.session.MediaBrowser, String, @IntRange(from=0) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+  }
+
+  public static final class MediaBrowser.Builder {
+    ctor public MediaBrowser.Builder(android.content.Context);
+    method public androidx.media2.session.MediaBrowser build();
+    method public androidx.media2.session.MediaBrowser.Builder setConnectionHints(android.os.Bundle);
+    method public androidx.media2.session.MediaBrowser.Builder setControllerCallback(java.util.concurrent.Executor, androidx.media2.session.MediaBrowser.BrowserCallback);
+    method public androidx.media2.session.MediaBrowser.Builder setSessionCompatToken(android.support.v4.media.session.MediaSessionCompat.Token);
+    method public androidx.media2.session.MediaBrowser.Builder setSessionToken(androidx.media2.session.SessionToken);
+  }
+
+  public class MediaConstants {
+    field public static final String MEDIA_URI_AUTHORITY = "media2-session";
+    field public static final String MEDIA_URI_PATH_PLAY_FROM_MEDIA_ID = "playFromMediaId";
+    field public static final String MEDIA_URI_PATH_PLAY_FROM_SEARCH = "playFromSearch";
+    field public static final String MEDIA_URI_PATH_PREPARE_FROM_MEDIA_ID = "prepareFromMediaId";
+    field public static final String MEDIA_URI_PATH_PREPARE_FROM_SEARCH = "prepareFromSearch";
+    field public static final String MEDIA_URI_QUERY_ID = "id";
+    field public static final String MEDIA_URI_QUERY_QUERY = "query";
+    field public static final String MEDIA_URI_SCHEME = "androidx";
+  }
+
+  public class MediaController implements java.io.Closeable {
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> addPlaylistItem(@IntRange(from=0) int, String);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> adjustVolume(int, int);
+    method public void close();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> deselectTrack(androidx.media2.common.SessionPlayer.TrackInfo);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> fastForward();
+    method public androidx.media2.session.SessionCommandGroup? getAllowedCommands();
+    method public long getBufferedPosition();
+    method @androidx.media2.common.SessionPlayer.BuffState public int getBufferingState();
+    method public androidx.media2.session.SessionToken? getConnectedToken();
+    method public androidx.media2.common.MediaItem? getCurrentMediaItem();
+    method public int getCurrentMediaItemIndex();
+    method public long getCurrentPosition();
+    method public long getDuration();
+    method public int getNextMediaItemIndex();
+    method public androidx.media2.session.MediaController.PlaybackInfo? getPlaybackInfo();
+    method public float getPlaybackSpeed();
+    method public int getPlayerState();
+    method public java.util.List<androidx.media2.common.MediaItem!>? getPlaylist();
+    method public androidx.media2.common.MediaMetadata? getPlaylistMetadata();
+    method public int getPreviousMediaItemIndex();
+    method @androidx.media2.common.SessionPlayer.RepeatMode public int getRepeatMode();
+    method public androidx.media2.common.SessionPlayer.TrackInfo? getSelectedTrack(@androidx.media2.common.SessionPlayer.TrackInfo.MediaTrackType int);
+    method public android.app.PendingIntent? getSessionActivity();
+    method @androidx.media2.common.SessionPlayer.ShuffleMode public int getShuffleMode();
+    method public java.util.List<androidx.media2.common.SessionPlayer.TrackInfo!> getTracks();
+    method public androidx.media2.common.VideoSize getVideoSize();
+    method public boolean isConnected();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> movePlaylistItem(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> pause();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> play();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> prepare();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> removePlaylistItem(@IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> replacePlaylistItem(@IntRange(from=0) int, String);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> rewind();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> seekTo(long);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> selectTrack(androidx.media2.common.SessionPlayer.TrackInfo);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> sendCustomCommand(androidx.media2.session.SessionCommand, android.os.Bundle?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setMediaItem(String);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setMediaUri(android.net.Uri, android.os.Bundle?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setPlaybackSpeed(float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setPlaylist(java.util.List<java.lang.String!>, androidx.media2.common.MediaMetadata?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setRating(String, androidx.media2.common.Rating);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setRepeatMode(@androidx.media2.common.SessionPlayer.RepeatMode int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setShuffleMode(@androidx.media2.common.SessionPlayer.ShuffleMode int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setSurface(android.view.Surface?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setVolumeTo(int, int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipBackward();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipForward();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipToNextPlaylistItem();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipToPlaylistItem(@IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipToPreviousPlaylistItem();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> updatePlaylistMetadata(androidx.media2.common.MediaMetadata?);
+  }
+
+  public static final class MediaController.Builder {
+    ctor public MediaController.Builder(android.content.Context);
+    method public androidx.media2.session.MediaController build();
+    method public androidx.media2.session.MediaController.Builder setConnectionHints(android.os.Bundle);
+    method public androidx.media2.session.MediaController.Builder setControllerCallback(java.util.concurrent.Executor, androidx.media2.session.MediaController.ControllerCallback);
+    method public androidx.media2.session.MediaController.Builder setSessionCompatToken(android.support.v4.media.session.MediaSessionCompat.Token);
+    method public androidx.media2.session.MediaController.Builder setSessionToken(androidx.media2.session.SessionToken);
+  }
+
+  public abstract static class MediaController.ControllerCallback {
+    ctor public MediaController.ControllerCallback();
+    method public void onAllowedCommandsChanged(androidx.media2.session.MediaController, androidx.media2.session.SessionCommandGroup);
+    method public void onBufferingStateChanged(androidx.media2.session.MediaController, androidx.media2.common.MediaItem, @androidx.media2.common.SessionPlayer.BuffState int);
+    method public void onConnected(androidx.media2.session.MediaController, androidx.media2.session.SessionCommandGroup);
+    method public void onCurrentMediaItemChanged(androidx.media2.session.MediaController, androidx.media2.common.MediaItem?);
+    method public androidx.media2.session.SessionResult onCustomCommand(androidx.media2.session.MediaController, androidx.media2.session.SessionCommand, android.os.Bundle?);
+    method public void onDisconnected(androidx.media2.session.MediaController);
+    method public void onPlaybackCompleted(androidx.media2.session.MediaController);
+    method public void onPlaybackInfoChanged(androidx.media2.session.MediaController, androidx.media2.session.MediaController.PlaybackInfo);
+    method public void onPlaybackSpeedChanged(androidx.media2.session.MediaController, float);
+    method public void onPlayerStateChanged(androidx.media2.session.MediaController, @androidx.media2.common.SessionPlayer.PlayerState int);
+    method public void onPlaylistChanged(androidx.media2.session.MediaController, java.util.List<androidx.media2.common.MediaItem!>?, androidx.media2.common.MediaMetadata?);
+    method public void onPlaylistMetadataChanged(androidx.media2.session.MediaController, androidx.media2.common.MediaMetadata?);
+    method public void onRepeatModeChanged(androidx.media2.session.MediaController, @androidx.media2.common.SessionPlayer.RepeatMode int);
+    method public void onSeekCompleted(androidx.media2.session.MediaController, long);
+    method public int onSetCustomLayout(androidx.media2.session.MediaController, java.util.List<androidx.media2.session.MediaSession.CommandButton!>);
+    method public void onShuffleModeChanged(androidx.media2.session.MediaController, @androidx.media2.common.SessionPlayer.ShuffleMode int);
+    method public void onSubtitleData(androidx.media2.session.MediaController, androidx.media2.common.MediaItem, androidx.media2.common.SessionPlayer.TrackInfo, androidx.media2.common.SubtitleData);
+    method public void onTrackDeselected(androidx.media2.session.MediaController, androidx.media2.common.SessionPlayer.TrackInfo);
+    method public void onTrackSelected(androidx.media2.session.MediaController, androidx.media2.common.SessionPlayer.TrackInfo);
+    method public void onTracksChanged(androidx.media2.session.MediaController, java.util.List<androidx.media2.common.SessionPlayer.TrackInfo!>);
+    method public void onVideoSizeChanged(androidx.media2.session.MediaController, androidx.media2.common.VideoSize);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public static final class MediaController.PlaybackInfo implements androidx.versionedparcelable.VersionedParcelable {
+    method public androidx.media.AudioAttributesCompat? getAudioAttributes();
+    method public int getControlType();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getPlaybackType();
+    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
+  }
+
+  public abstract class MediaLibraryService extends androidx.media2.session.MediaSessionService {
+    ctor public MediaLibraryService();
+    method public abstract androidx.media2.session.MediaLibraryService.MediaLibrarySession? onGetSession(androidx.media2.session.MediaSession.ControllerInfo);
+    field public static final String SERVICE_INTERFACE = "androidx.media2.session.MediaLibraryService";
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public static final class MediaLibraryService.LibraryParams implements androidx.versionedparcelable.VersionedParcelable {
+    method public android.os.Bundle? getExtras();
+    method public boolean isOffline();
+    method public boolean isRecent();
+    method public boolean isSuggested();
+  }
+
+  public static final class MediaLibraryService.LibraryParams.Builder {
+    ctor public MediaLibraryService.LibraryParams.Builder();
+    method public androidx.media2.session.MediaLibraryService.LibraryParams build();
+    method public androidx.media2.session.MediaLibraryService.LibraryParams.Builder setExtras(android.os.Bundle?);
+    method public androidx.media2.session.MediaLibraryService.LibraryParams.Builder setOffline(boolean);
+    method public androidx.media2.session.MediaLibraryService.LibraryParams.Builder setRecent(boolean);
+    method public androidx.media2.session.MediaLibraryService.LibraryParams.Builder setSuggested(boolean);
+  }
+
+  public static final class MediaLibraryService.MediaLibrarySession extends androidx.media2.session.MediaSession {
+    method public void notifyChildrenChanged(androidx.media2.session.MediaSession.ControllerInfo, String, @IntRange(from=0) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public void notifyChildrenChanged(String, int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public void notifySearchResultChanged(androidx.media2.session.MediaSession.ControllerInfo, String, @IntRange(from=0) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+  }
+
+  public static final class MediaLibraryService.MediaLibrarySession.Builder {
+    ctor public MediaLibraryService.MediaLibrarySession.Builder(androidx.media2.session.MediaLibraryService, androidx.media2.common.SessionPlayer, java.util.concurrent.Executor, androidx.media2.session.MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback);
+    method public androidx.media2.session.MediaLibraryService.MediaLibrarySession build();
+    method public androidx.media2.session.MediaLibraryService.MediaLibrarySession.Builder setExtras(android.os.Bundle);
+    method public androidx.media2.session.MediaLibraryService.MediaLibrarySession.Builder setId(String);
+    method public androidx.media2.session.MediaLibraryService.MediaLibrarySession.Builder setSessionActivity(android.app.PendingIntent?);
+  }
+
+  public static class MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback extends androidx.media2.session.MediaSession.SessionCallback {
+    ctor public MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback();
+    method public androidx.media2.session.LibraryResult onGetChildren(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String, @IntRange(from=0) int, @IntRange(from=1) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public androidx.media2.session.LibraryResult onGetItem(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String);
+    method public androidx.media2.session.LibraryResult onGetLibraryRoot(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public androidx.media2.session.LibraryResult onGetSearchResult(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String, @IntRange(from=0) int, @IntRange(from=1) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public int onSearch(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public int onSubscribe(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public int onUnsubscribe(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String);
+  }
+
+  public class MediaSession implements java.io.Closeable {
+    method public void broadcastCustomCommand(androidx.media2.session.SessionCommand, android.os.Bundle?);
+    method public void close();
+    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.Token getSessionCompatToken();
+    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);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setCustomLayout(androidx.media2.session.MediaSession.ControllerInfo, java.util.List<androidx.media2.session.MediaSession.CommandButton!>);
+    method public void updatePlayer(androidx.media2.common.SessionPlayer);
+  }
+
+  public static final class MediaSession.Builder {
+    ctor public MediaSession.Builder(android.content.Context, androidx.media2.common.SessionPlayer);
+    method public androidx.media2.session.MediaSession build();
+    method public androidx.media2.session.MediaSession.Builder setExtras(android.os.Bundle);
+    method public androidx.media2.session.MediaSession.Builder setId(String);
+    method public androidx.media2.session.MediaSession.Builder setSessionActivity(android.app.PendingIntent?);
+    method public androidx.media2.session.MediaSession.Builder setSessionCallback(java.util.concurrent.Executor, androidx.media2.session.MediaSession.SessionCallback);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public static final class MediaSession.CommandButton implements androidx.versionedparcelable.VersionedParcelable {
+    method public androidx.media2.session.SessionCommand? getCommand();
+    method public CharSequence? getDisplayName();
+    method public android.os.Bundle? getExtras();
+    method public int getIconResId();
+    method public boolean isEnabled();
+  }
+
+  public static final class MediaSession.CommandButton.Builder {
+    ctor public MediaSession.CommandButton.Builder();
+    method public androidx.media2.session.MediaSession.CommandButton build();
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setCommand(androidx.media2.session.SessionCommand?);
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setDisplayName(CharSequence?);
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setEnabled(boolean);
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setExtras(android.os.Bundle?);
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setIconResId(int);
+  }
+
+  public static final class MediaSession.ControllerInfo {
+    method public android.os.Bundle getConnectionHints();
+    method public String getPackageName();
+    method public int getUid();
+  }
+
+  public abstract static class MediaSession.SessionCallback {
+    ctor public MediaSession.SessionCallback();
+    method public int onCommandRequest(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, androidx.media2.session.SessionCommand);
+    method public androidx.media2.session.SessionCommandGroup? onConnect(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public androidx.media2.common.MediaItem? onCreateMediaItem(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, String);
+    method public androidx.media2.session.SessionResult onCustomCommand(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, androidx.media2.session.SessionCommand, android.os.Bundle?);
+    method public void onDisconnected(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public int onFastForward(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public void onPostConnect(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public int onRewind(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public int onSetMediaUri(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, android.net.Uri, android.os.Bundle?);
+    method public int onSetRating(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, String, androidx.media2.common.Rating);
+    method public int onSkipBackward(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public int onSkipForward(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+  }
+
+  public final class MediaSessionManager {
+    method public static androidx.media2.session.MediaSessionManager getInstance(android.content.Context);
+    method public java.util.Set<androidx.media2.session.SessionToken!> getSessionServiceTokens();
+  }
+
+  public abstract class MediaSessionService extends android.app.Service {
+    ctor public MediaSessionService();
+    method public final void addSession(androidx.media2.session.MediaSession);
+    method public final java.util.List<androidx.media2.session.MediaSession!> getSessions();
+    method @CallSuper public android.os.IBinder? onBind(android.content.Intent);
+    method public abstract androidx.media2.session.MediaSession? onGetSession(androidx.media2.session.MediaSession.ControllerInfo);
+    method public androidx.media2.session.MediaSessionService.MediaNotification? onUpdateNotification(androidx.media2.session.MediaSession);
+    method public final void removeSession(androidx.media2.session.MediaSession);
+    field public static final String SERVICE_INTERFACE = "androidx.media2.session.MediaSessionService";
+  }
+
+  public static class MediaSessionService.MediaNotification {
+    ctor public MediaSessionService.MediaNotification(int, android.app.Notification);
+    method public android.app.Notification getNotification();
+    method public int getNotificationId();
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class PercentageRating implements androidx.media2.common.Rating {
+    ctor public PercentageRating();
+    ctor public PercentageRating(float);
+    method public float getPercentRating();
+    method public boolean isRated();
+  }
+
+  public abstract class RemoteSessionPlayer extends androidx.media2.common.SessionPlayer {
+    ctor public RemoteSessionPlayer();
+    method public abstract java.util.concurrent.Future<androidx.media2.common.SessionPlayer.PlayerResult!> adjustVolume(int);
+    method public abstract int getMaxVolume();
+    method public abstract int getVolume();
+    method public abstract int getVolumeControlType();
+    method public abstract java.util.concurrent.Future<androidx.media2.common.SessionPlayer.PlayerResult!> setVolume(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
+  public static class RemoteSessionPlayer.Callback extends androidx.media2.common.SessionPlayer.PlayerCallback {
+    ctor public RemoteSessionPlayer.Callback();
+    method public void onVolumeChanged(androidx.media2.session.RemoteSessionPlayer, int);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class SessionCommand implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SessionCommand(int);
+    ctor public SessionCommand(String, android.os.Bundle?);
+    method public int getCommandCode();
+    method public String? getCustomAction();
+    method public android.os.Bundle? getCustomExtras();
+    field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
+    field public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 50003; // 0xc353
+    field public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 50004; // 0xc354
+    field public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 50000; // 0xc350
+    field public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 50006; // 0xc356
+    field public static final int COMMAND_CODE_LIBRARY_SEARCH = 50005; // 0xc355
+    field public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 50001; // 0xc351
+    field public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 50002; // 0xc352
+    field public static final int COMMAND_CODE_PLAYER_ADD_PLAYLIST_ITEM = 10013; // 0x271d
+    field public static final int COMMAND_CODE_PLAYER_DESELECT_TRACK = 11002; // 0x2afa
+    field public static final int COMMAND_CODE_PLAYER_GET_CURRENT_MEDIA_ITEM = 10016; // 0x2720
+    field public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST = 10005; // 0x2715
+    field public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST_METADATA = 10012; // 0x271c
+    field public static final int COMMAND_CODE_PLAYER_MOVE_PLAYLIST_ITEM = 10019; // 0x2723
+    field public static final int COMMAND_CODE_PLAYER_PAUSE = 10001; // 0x2711
+    field public static final int COMMAND_CODE_PLAYER_PLAY = 10000; // 0x2710
+    field public static final int COMMAND_CODE_PLAYER_PREPARE = 10002; // 0x2712
+    field public static final int COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM = 10014; // 0x271e
+    field public static final int COMMAND_CODE_PLAYER_REPLACE_PLAYLIST_ITEM = 10015; // 0x271f
+    field public static final int COMMAND_CODE_PLAYER_SEEK_TO = 10003; // 0x2713
+    field public static final int COMMAND_CODE_PLAYER_SELECT_TRACK = 11001; // 0x2af9
+    field public static final int COMMAND_CODE_PLAYER_SET_MEDIA_ITEM = 10018; // 0x2722
+    field public static final int COMMAND_CODE_PLAYER_SET_PLAYLIST = 10006; // 0x2716
+    field public static final int COMMAND_CODE_PLAYER_SET_REPEAT_MODE = 10011; // 0x271b
+    field public static final int COMMAND_CODE_PLAYER_SET_SHUFFLE_MODE = 10010; // 0x271a
+    field public static final int COMMAND_CODE_PLAYER_SET_SPEED = 10004; // 0x2714
+    field public static final int COMMAND_CODE_PLAYER_SET_SURFACE = 11000; // 0x2af8
+    field public static final int COMMAND_CODE_PLAYER_SKIP_TO_NEXT_PLAYLIST_ITEM = 10009; // 0x2719
+    field public static final int COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM = 10007; // 0x2717
+    field public static final int COMMAND_CODE_PLAYER_SKIP_TO_PREVIOUS_PLAYLIST_ITEM = 10008; // 0x2718
+    field public static final int COMMAND_CODE_PLAYER_UPDATE_LIST_METADATA = 10017; // 0x2721
+    field public static final int COMMAND_CODE_SESSION_FAST_FORWARD = 40000; // 0x9c40
+    field public static final int COMMAND_CODE_SESSION_REWIND = 40001; // 0x9c41
+    field public static final int COMMAND_CODE_SESSION_SET_MEDIA_URI = 40011; // 0x9c4b
+    field public static final int COMMAND_CODE_SESSION_SET_RATING = 40010; // 0x9c4a
+    field public static final int COMMAND_CODE_SESSION_SKIP_BACKWARD = 40003; // 0x9c43
+    field public static final int COMMAND_CODE_SESSION_SKIP_FORWARD = 40002; // 0x9c42
+    field public static final int COMMAND_CODE_VOLUME_ADJUST_VOLUME = 30001; // 0x7531
+    field public static final int COMMAND_CODE_VOLUME_SET_VOLUME = 30000; // 0x7530
+    field public static final int COMMAND_VERSION_1 = 1; // 0x1
+    field public static final int COMMAND_VERSION_2 = 2; // 0x2
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class SessionCommandGroup implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SessionCommandGroup();
+    ctor public SessionCommandGroup(java.util.Collection<androidx.media2.session.SessionCommand!>?);
+    method public java.util.Set<androidx.media2.session.SessionCommand!> getCommands();
+    method public boolean hasCommand(androidx.media2.session.SessionCommand);
+    method public boolean hasCommand(int);
+  }
+
+  public static final class SessionCommandGroup.Builder {
+    ctor public SessionCommandGroup.Builder();
+    ctor public SessionCommandGroup.Builder(androidx.media2.session.SessionCommandGroup);
+    method public androidx.media2.session.SessionCommandGroup.Builder addAllPredefinedCommands(int);
+    method public androidx.media2.session.SessionCommandGroup.Builder addCommand(androidx.media2.session.SessionCommand);
+    method public androidx.media2.session.SessionCommandGroup build();
+    method public androidx.media2.session.SessionCommandGroup.Builder removeCommand(androidx.media2.session.SessionCommand);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(isCustom=true) public class SessionResult extends androidx.versionedparcelable.CustomVersionedParcelable implements androidx.media2.common.BaseResult {
+    ctor public SessionResult(int, android.os.Bundle?);
+    method public long getCompletionTime();
+    method public android.os.Bundle? getCustomCommandResult();
+    method public androidx.media2.common.MediaItem? getMediaItem();
+    method public int getResultCode();
+    field public static final int RESULT_ERROR_SESSION_AUTHENTICATION_EXPIRED = -102; // 0xffffff9a
+    field public static final int RESULT_ERROR_SESSION_CONCURRENT_STREAM_LIMIT = -104; // 0xffffff98
+    field public static final int RESULT_ERROR_SESSION_DISCONNECTED = -100; // 0xffffff9c
+    field public static final int RESULT_ERROR_SESSION_NOT_AVAILABLE_IN_REGION = -106; // 0xffffff96
+    field public static final int RESULT_ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED = -105; // 0xffffff97
+    field public static final int RESULT_ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED = -103; // 0xffffff99
+    field public static final int RESULT_ERROR_SESSION_SETUP_REQUIRED = -108; // 0xffffff94
+    field public static final int RESULT_ERROR_SESSION_SKIP_LIMIT_REACHED = -107; // 0xffffff95
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class SessionToken implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SessionToken(android.content.Context, android.content.ComponentName);
+    method public android.os.Bundle getExtras();
+    method public String getPackageName();
+    method public String? getServiceName();
+    method public int getType();
+    method public int getUid();
+    field public static final int TYPE_LIBRARY_SERVICE = 2; // 0x2
+    field public static final int TYPE_SESSION = 0; // 0x0
+    field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class StarRating implements androidx.media2.common.Rating {
+    ctor public StarRating(@IntRange(from=1) int);
+    ctor public StarRating(@IntRange(from=1) int, float);
+    method public int getMaxStars();
+    method public float getStarRating();
+    method public boolean isRated();
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class ThumbRating implements androidx.media2.common.Rating {
+    ctor public ThumbRating();
+    ctor public ThumbRating(boolean);
+    method public boolean isRated();
+    method public boolean isThumbUp();
+  }
+
+}
+
diff --git a/media2/session/api/res-1.1.0-beta01.txt b/media2/session/api/res-1.1.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/media2/session/api/res-1.1.0-beta01.txt
diff --git a/media2/session/api/restricted_1.1.0-beta01.txt b/media2/session/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..646d255
--- /dev/null
+++ b/media2/session/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,432 @@
+// Signature format: 3.0
+package androidx.media2.session {
+
+  @androidx.versionedparcelable.VersionedParcelize public final class HeartRating implements androidx.media2.common.Rating {
+    ctor public HeartRating();
+    ctor public HeartRating(boolean);
+    method public boolean hasHeart();
+    method public boolean isRated();
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(isCustom=true) public class LibraryResult extends androidx.versionedparcelable.CustomVersionedParcelable implements androidx.media2.common.BaseResult {
+    ctor public LibraryResult(int);
+    ctor public LibraryResult(int, androidx.media2.common.MediaItem?, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    ctor public LibraryResult(int, java.util.List<androidx.media2.common.MediaItem!>?, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public long getCompletionTime();
+    method public androidx.media2.session.MediaLibraryService.LibraryParams? getLibraryParams();
+    method public androidx.media2.common.MediaItem? getMediaItem();
+    method public java.util.List<androidx.media2.common.MediaItem!>? getMediaItems();
+    method public int getResultCode();
+    field public static final int RESULT_ERROR_SESSION_AUTHENTICATION_EXPIRED = -102; // 0xffffff9a
+    field public static final int RESULT_ERROR_SESSION_CONCURRENT_STREAM_LIMIT = -104; // 0xffffff98
+    field public static final int RESULT_ERROR_SESSION_DISCONNECTED = -100; // 0xffffff9c
+    field public static final int RESULT_ERROR_SESSION_NOT_AVAILABLE_IN_REGION = -106; // 0xffffff96
+    field public static final int RESULT_ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED = -105; // 0xffffff97
+    field public static final int RESULT_ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED = -103; // 0xffffff99
+    field public static final int RESULT_ERROR_SESSION_SETUP_REQUIRED = -108; // 0xffffff94
+    field public static final int RESULT_ERROR_SESSION_SKIP_LIMIT_REACHED = -107; // 0xffffff95
+  }
+
+  public class MediaBrowser extends androidx.media2.session.MediaController {
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> getChildren(String, @IntRange(from=0) int, @IntRange(from=1) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> getItem(String);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> getLibraryRoot(androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> getSearchResult(String, @IntRange(from=0) int, @IntRange(from=1) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> search(String, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> subscribe(String, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.LibraryResult!> unsubscribe(String);
+  }
+
+  public static class MediaBrowser.BrowserCallback extends androidx.media2.session.MediaController.ControllerCallback {
+    ctor public MediaBrowser.BrowserCallback();
+    method public void onChildrenChanged(androidx.media2.session.MediaBrowser, String, @IntRange(from=0) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public void onSearchResultChanged(androidx.media2.session.MediaBrowser, String, @IntRange(from=0) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+  }
+
+  public static final class MediaBrowser.Builder {
+    ctor public MediaBrowser.Builder(android.content.Context);
+    method public androidx.media2.session.MediaBrowser build();
+    method public androidx.media2.session.MediaBrowser.Builder setConnectionHints(android.os.Bundle);
+    method public androidx.media2.session.MediaBrowser.Builder setControllerCallback(java.util.concurrent.Executor, androidx.media2.session.MediaBrowser.BrowserCallback);
+    method public androidx.media2.session.MediaBrowser.Builder setSessionCompatToken(android.support.v4.media.session.MediaSessionCompat.Token);
+    method public androidx.media2.session.MediaBrowser.Builder setSessionToken(androidx.media2.session.SessionToken);
+  }
+
+  public class MediaConstants {
+    field public static final String MEDIA_URI_AUTHORITY = "media2-session";
+    field public static final String MEDIA_URI_PATH_PLAY_FROM_MEDIA_ID = "playFromMediaId";
+    field public static final String MEDIA_URI_PATH_PLAY_FROM_SEARCH = "playFromSearch";
+    field public static final String MEDIA_URI_PATH_PREPARE_FROM_MEDIA_ID = "prepareFromMediaId";
+    field public static final String MEDIA_URI_PATH_PREPARE_FROM_SEARCH = "prepareFromSearch";
+    field public static final String MEDIA_URI_QUERY_ID = "id";
+    field public static final String MEDIA_URI_QUERY_QUERY = "query";
+    field public static final String MEDIA_URI_SCHEME = "androidx";
+  }
+
+  public class MediaController implements java.io.Closeable {
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> addPlaylistItem(@IntRange(from=0) int, String);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> adjustVolume(int, int);
+    method public void close();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> deselectTrack(androidx.media2.common.SessionPlayer.TrackInfo);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> fastForward();
+    method public androidx.media2.session.SessionCommandGroup? getAllowedCommands();
+    method public long getBufferedPosition();
+    method @androidx.media2.common.SessionPlayer.BuffState public int getBufferingState();
+    method public androidx.media2.session.SessionToken? getConnectedToken();
+    method public androidx.media2.common.MediaItem? getCurrentMediaItem();
+    method public int getCurrentMediaItemIndex();
+    method public long getCurrentPosition();
+    method public long getDuration();
+    method public int getNextMediaItemIndex();
+    method public androidx.media2.session.MediaController.PlaybackInfo? getPlaybackInfo();
+    method public float getPlaybackSpeed();
+    method public int getPlayerState();
+    method public java.util.List<androidx.media2.common.MediaItem!>? getPlaylist();
+    method public androidx.media2.common.MediaMetadata? getPlaylistMetadata();
+    method public int getPreviousMediaItemIndex();
+    method @androidx.media2.common.SessionPlayer.RepeatMode public int getRepeatMode();
+    method public androidx.media2.common.SessionPlayer.TrackInfo? getSelectedTrack(@androidx.media2.common.SessionPlayer.TrackInfo.MediaTrackType int);
+    method public android.app.PendingIntent? getSessionActivity();
+    method @androidx.media2.common.SessionPlayer.ShuffleMode public int getShuffleMode();
+    method public java.util.List<androidx.media2.common.SessionPlayer.TrackInfo!> getTracks();
+    method public androidx.media2.common.VideoSize getVideoSize();
+    method public boolean isConnected();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> movePlaylistItem(@IntRange(from=0) int, @IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> pause();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> play();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> prepare();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> removePlaylistItem(@IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> replacePlaylistItem(@IntRange(from=0) int, String);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> rewind();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> seekTo(long);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> selectTrack(androidx.media2.common.SessionPlayer.TrackInfo);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> sendCustomCommand(androidx.media2.session.SessionCommand, android.os.Bundle?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setMediaItem(String);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setMediaUri(android.net.Uri, android.os.Bundle?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setPlaybackSpeed(float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setPlaylist(java.util.List<java.lang.String!>, androidx.media2.common.MediaMetadata?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setRating(String, androidx.media2.common.Rating);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setRepeatMode(@androidx.media2.common.SessionPlayer.RepeatMode int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setShuffleMode(@androidx.media2.common.SessionPlayer.ShuffleMode int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setSurface(android.view.Surface?);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setVolumeTo(int, int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipBackward();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipForward();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipToNextPlaylistItem();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipToPlaylistItem(@IntRange(from=0) int);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> skipToPreviousPlaylistItem();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> updatePlaylistMetadata(androidx.media2.common.MediaMetadata?);
+  }
+
+  public static final class MediaController.Builder {
+    ctor public MediaController.Builder(android.content.Context);
+    method public androidx.media2.session.MediaController build();
+    method public androidx.media2.session.MediaController.Builder setConnectionHints(android.os.Bundle);
+    method public androidx.media2.session.MediaController.Builder setControllerCallback(java.util.concurrent.Executor, androidx.media2.session.MediaController.ControllerCallback);
+    method public androidx.media2.session.MediaController.Builder setSessionCompatToken(android.support.v4.media.session.MediaSessionCompat.Token);
+    method public androidx.media2.session.MediaController.Builder setSessionToken(androidx.media2.session.SessionToken);
+  }
+
+  public abstract static class MediaController.ControllerCallback {
+    ctor public MediaController.ControllerCallback();
+    method public void onAllowedCommandsChanged(androidx.media2.session.MediaController, androidx.media2.session.SessionCommandGroup);
+    method public void onBufferingStateChanged(androidx.media2.session.MediaController, androidx.media2.common.MediaItem, @androidx.media2.common.SessionPlayer.BuffState int);
+    method public void onConnected(androidx.media2.session.MediaController, androidx.media2.session.SessionCommandGroup);
+    method public void onCurrentMediaItemChanged(androidx.media2.session.MediaController, androidx.media2.common.MediaItem?);
+    method public androidx.media2.session.SessionResult onCustomCommand(androidx.media2.session.MediaController, androidx.media2.session.SessionCommand, android.os.Bundle?);
+    method public void onDisconnected(androidx.media2.session.MediaController);
+    method public void onPlaybackCompleted(androidx.media2.session.MediaController);
+    method public void onPlaybackInfoChanged(androidx.media2.session.MediaController, androidx.media2.session.MediaController.PlaybackInfo);
+    method public void onPlaybackSpeedChanged(androidx.media2.session.MediaController, float);
+    method public void onPlayerStateChanged(androidx.media2.session.MediaController, @androidx.media2.common.SessionPlayer.PlayerState int);
+    method public void onPlaylistChanged(androidx.media2.session.MediaController, java.util.List<androidx.media2.common.MediaItem!>?, androidx.media2.common.MediaMetadata?);
+    method public void onPlaylistMetadataChanged(androidx.media2.session.MediaController, androidx.media2.common.MediaMetadata?);
+    method public void onRepeatModeChanged(androidx.media2.session.MediaController, @androidx.media2.common.SessionPlayer.RepeatMode int);
+    method public void onSeekCompleted(androidx.media2.session.MediaController, long);
+    method public int onSetCustomLayout(androidx.media2.session.MediaController, java.util.List<androidx.media2.session.MediaSession.CommandButton!>);
+    method public void onShuffleModeChanged(androidx.media2.session.MediaController, @androidx.media2.common.SessionPlayer.ShuffleMode int);
+    method public void onSubtitleData(androidx.media2.session.MediaController, androidx.media2.common.MediaItem, androidx.media2.common.SessionPlayer.TrackInfo, androidx.media2.common.SubtitleData);
+    method public void onTrackDeselected(androidx.media2.session.MediaController, androidx.media2.common.SessionPlayer.TrackInfo);
+    method public void onTrackSelected(androidx.media2.session.MediaController, androidx.media2.common.SessionPlayer.TrackInfo);
+    method public void onTracksChanged(androidx.media2.session.MediaController, java.util.List<androidx.media2.common.SessionPlayer.TrackInfo!>);
+    method public void onVideoSizeChanged(androidx.media2.session.MediaController, androidx.media2.common.VideoSize);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public static final class MediaController.PlaybackInfo implements androidx.versionedparcelable.VersionedParcelable {
+    method public androidx.media.AudioAttributesCompat? getAudioAttributes();
+    method public int getControlType();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getPlaybackType();
+    field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
+    field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
+  }
+
+  public abstract class MediaLibraryService extends androidx.media2.session.MediaSessionService {
+    ctor public MediaLibraryService();
+    method public abstract androidx.media2.session.MediaLibraryService.MediaLibrarySession? onGetSession(androidx.media2.session.MediaSession.ControllerInfo);
+    field public static final String SERVICE_INTERFACE = "androidx.media2.session.MediaLibraryService";
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public static final class MediaLibraryService.LibraryParams implements androidx.versionedparcelable.VersionedParcelable {
+    method public android.os.Bundle? getExtras();
+    method public boolean isOffline();
+    method public boolean isRecent();
+    method public boolean isSuggested();
+  }
+
+  public static final class MediaLibraryService.LibraryParams.Builder {
+    ctor public MediaLibraryService.LibraryParams.Builder();
+    method public androidx.media2.session.MediaLibraryService.LibraryParams build();
+    method public androidx.media2.session.MediaLibraryService.LibraryParams.Builder setExtras(android.os.Bundle?);
+    method public androidx.media2.session.MediaLibraryService.LibraryParams.Builder setOffline(boolean);
+    method public androidx.media2.session.MediaLibraryService.LibraryParams.Builder setRecent(boolean);
+    method public androidx.media2.session.MediaLibraryService.LibraryParams.Builder setSuggested(boolean);
+  }
+
+  public static final class MediaLibraryService.MediaLibrarySession extends androidx.media2.session.MediaSession {
+    method public void notifyChildrenChanged(androidx.media2.session.MediaSession.ControllerInfo, String, @IntRange(from=0) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public void notifyChildrenChanged(String, int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public void notifySearchResultChanged(androidx.media2.session.MediaSession.ControllerInfo, String, @IntRange(from=0) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+  }
+
+  public static final class MediaLibraryService.MediaLibrarySession.Builder {
+    ctor public MediaLibraryService.MediaLibrarySession.Builder(androidx.media2.session.MediaLibraryService, androidx.media2.common.SessionPlayer, java.util.concurrent.Executor, androidx.media2.session.MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback);
+    method public androidx.media2.session.MediaLibraryService.MediaLibrarySession build();
+    method public androidx.media2.session.MediaLibraryService.MediaLibrarySession.Builder setExtras(android.os.Bundle);
+    method public androidx.media2.session.MediaLibraryService.MediaLibrarySession.Builder setId(String);
+    method public androidx.media2.session.MediaLibraryService.MediaLibrarySession.Builder setSessionActivity(android.app.PendingIntent?);
+  }
+
+  public static class MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback extends androidx.media2.session.MediaSession.SessionCallback {
+    ctor public MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback();
+    method public androidx.media2.session.LibraryResult onGetChildren(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String, @IntRange(from=0) int, @IntRange(from=1) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public androidx.media2.session.LibraryResult onGetItem(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String);
+    method public androidx.media2.session.LibraryResult onGetLibraryRoot(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public androidx.media2.session.LibraryResult onGetSearchResult(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String, @IntRange(from=0) int, @IntRange(from=1) int, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public int onSearch(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public int onSubscribe(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String, androidx.media2.session.MediaLibraryService.LibraryParams?);
+    method public int onUnsubscribe(androidx.media2.session.MediaLibraryService.MediaLibrarySession, androidx.media2.session.MediaSession.ControllerInfo, String);
+  }
+
+  public class MediaSession implements java.io.Closeable {
+    method public void broadcastCustomCommand(androidx.media2.session.SessionCommand, android.os.Bundle?);
+    method public void close();
+    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.Token getSessionCompatToken();
+    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);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.media2.session.SessionResult!> setCustomLayout(androidx.media2.session.MediaSession.ControllerInfo, java.util.List<androidx.media2.session.MediaSession.CommandButton!>);
+    method public void updatePlayer(androidx.media2.common.SessionPlayer);
+  }
+
+  public static final class MediaSession.Builder {
+    ctor public MediaSession.Builder(android.content.Context, androidx.media2.common.SessionPlayer);
+    method public androidx.media2.session.MediaSession build();
+    method public androidx.media2.session.MediaSession.Builder setExtras(android.os.Bundle);
+    method public androidx.media2.session.MediaSession.Builder setId(String);
+    method public androidx.media2.session.MediaSession.Builder setSessionActivity(android.app.PendingIntent?);
+    method public androidx.media2.session.MediaSession.Builder setSessionCallback(java.util.concurrent.Executor, androidx.media2.session.MediaSession.SessionCallback);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public static final class MediaSession.CommandButton implements androidx.versionedparcelable.VersionedParcelable {
+    method public androidx.media2.session.SessionCommand? getCommand();
+    method public CharSequence? getDisplayName();
+    method public android.os.Bundle? getExtras();
+    method public int getIconResId();
+    method public boolean isEnabled();
+  }
+
+  public static final class MediaSession.CommandButton.Builder {
+    ctor public MediaSession.CommandButton.Builder();
+    method public androidx.media2.session.MediaSession.CommandButton build();
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setCommand(androidx.media2.session.SessionCommand?);
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setDisplayName(CharSequence?);
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setEnabled(boolean);
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setExtras(android.os.Bundle?);
+    method public androidx.media2.session.MediaSession.CommandButton.Builder setIconResId(int);
+  }
+
+  public static final class MediaSession.ControllerInfo {
+    method public android.os.Bundle getConnectionHints();
+    method public String getPackageName();
+    method public int getUid();
+  }
+
+  public abstract static class MediaSession.SessionCallback {
+    ctor public MediaSession.SessionCallback();
+    method public int onCommandRequest(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, androidx.media2.session.SessionCommand);
+    method public androidx.media2.session.SessionCommandGroup? onConnect(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public androidx.media2.common.MediaItem? onCreateMediaItem(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, String);
+    method public androidx.media2.session.SessionResult onCustomCommand(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, androidx.media2.session.SessionCommand, android.os.Bundle?);
+    method public void onDisconnected(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public int onFastForward(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public void onPostConnect(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public int onRewind(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public int onSetMediaUri(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, android.net.Uri, android.os.Bundle?);
+    method public int onSetRating(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo, String, androidx.media2.common.Rating);
+    method public int onSkipBackward(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+    method public int onSkipForward(androidx.media2.session.MediaSession, androidx.media2.session.MediaSession.ControllerInfo);
+  }
+
+  public final class MediaSessionManager {
+    method public static androidx.media2.session.MediaSessionManager getInstance(android.content.Context);
+    method public java.util.Set<androidx.media2.session.SessionToken!> getSessionServiceTokens();
+  }
+
+  public abstract class MediaSessionService extends android.app.Service {
+    ctor public MediaSessionService();
+    method public final void addSession(androidx.media2.session.MediaSession);
+    method public final java.util.List<androidx.media2.session.MediaSession!> getSessions();
+    method @CallSuper public android.os.IBinder? onBind(android.content.Intent);
+    method public abstract androidx.media2.session.MediaSession? onGetSession(androidx.media2.session.MediaSession.ControllerInfo);
+    method public androidx.media2.session.MediaSessionService.MediaNotification? onUpdateNotification(androidx.media2.session.MediaSession);
+    method public final void removeSession(androidx.media2.session.MediaSession);
+    field public static final String SERVICE_INTERFACE = "androidx.media2.session.MediaSessionService";
+  }
+
+  public static class MediaSessionService.MediaNotification {
+    ctor public MediaSessionService.MediaNotification(int, android.app.Notification);
+    method public android.app.Notification getNotification();
+    method public int getNotificationId();
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class PercentageRating implements androidx.media2.common.Rating {
+    ctor public PercentageRating();
+    ctor public PercentageRating(float);
+    method public float getPercentRating();
+    method public boolean isRated();
+  }
+
+  public abstract class RemoteSessionPlayer extends androidx.media2.common.SessionPlayer {
+    ctor public RemoteSessionPlayer();
+    method public abstract java.util.concurrent.Future<androidx.media2.common.SessionPlayer.PlayerResult!> adjustVolume(int);
+    method public abstract int getMaxVolume();
+    method public abstract int getVolume();
+    method public abstract int getVolumeControlType();
+    method public abstract java.util.concurrent.Future<androidx.media2.common.SessionPlayer.PlayerResult!> setVolume(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
+  public static class RemoteSessionPlayer.Callback extends androidx.media2.common.SessionPlayer.PlayerCallback {
+    ctor public RemoteSessionPlayer.Callback();
+    method public void onVolumeChanged(androidx.media2.session.RemoteSessionPlayer, int);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class SessionCommand implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SessionCommand(int);
+    ctor public SessionCommand(String, android.os.Bundle?);
+    method public int getCommandCode();
+    method public String? getCustomAction();
+    method public android.os.Bundle? getCustomExtras();
+    field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0
+    field public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 50003; // 0xc353
+    field public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 50004; // 0xc354
+    field public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 50000; // 0xc350
+    field public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 50006; // 0xc356
+    field public static final int COMMAND_CODE_LIBRARY_SEARCH = 50005; // 0xc355
+    field public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 50001; // 0xc351
+    field public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 50002; // 0xc352
+    field public static final int COMMAND_CODE_PLAYER_ADD_PLAYLIST_ITEM = 10013; // 0x271d
+    field public static final int COMMAND_CODE_PLAYER_DESELECT_TRACK = 11002; // 0x2afa
+    field public static final int COMMAND_CODE_PLAYER_GET_CURRENT_MEDIA_ITEM = 10016; // 0x2720
+    field public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST = 10005; // 0x2715
+    field public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST_METADATA = 10012; // 0x271c
+    field public static final int COMMAND_CODE_PLAYER_MOVE_PLAYLIST_ITEM = 10019; // 0x2723
+    field public static final int COMMAND_CODE_PLAYER_PAUSE = 10001; // 0x2711
+    field public static final int COMMAND_CODE_PLAYER_PLAY = 10000; // 0x2710
+    field public static final int COMMAND_CODE_PLAYER_PREPARE = 10002; // 0x2712
+    field public static final int COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM = 10014; // 0x271e
+    field public static final int COMMAND_CODE_PLAYER_REPLACE_PLAYLIST_ITEM = 10015; // 0x271f
+    field public static final int COMMAND_CODE_PLAYER_SEEK_TO = 10003; // 0x2713
+    field public static final int COMMAND_CODE_PLAYER_SELECT_TRACK = 11001; // 0x2af9
+    field public static final int COMMAND_CODE_PLAYER_SET_MEDIA_ITEM = 10018; // 0x2722
+    field public static final int COMMAND_CODE_PLAYER_SET_PLAYLIST = 10006; // 0x2716
+    field public static final int COMMAND_CODE_PLAYER_SET_REPEAT_MODE = 10011; // 0x271b
+    field public static final int COMMAND_CODE_PLAYER_SET_SHUFFLE_MODE = 10010; // 0x271a
+    field public static final int COMMAND_CODE_PLAYER_SET_SPEED = 10004; // 0x2714
+    field public static final int COMMAND_CODE_PLAYER_SET_SURFACE = 11000; // 0x2af8
+    field public static final int COMMAND_CODE_PLAYER_SKIP_TO_NEXT_PLAYLIST_ITEM = 10009; // 0x2719
+    field public static final int COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM = 10007; // 0x2717
+    field public static final int COMMAND_CODE_PLAYER_SKIP_TO_PREVIOUS_PLAYLIST_ITEM = 10008; // 0x2718
+    field public static final int COMMAND_CODE_PLAYER_UPDATE_LIST_METADATA = 10017; // 0x2721
+    field public static final int COMMAND_CODE_SESSION_FAST_FORWARD = 40000; // 0x9c40
+    field public static final int COMMAND_CODE_SESSION_REWIND = 40001; // 0x9c41
+    field public static final int COMMAND_CODE_SESSION_SET_MEDIA_URI = 40011; // 0x9c4b
+    field public static final int COMMAND_CODE_SESSION_SET_RATING = 40010; // 0x9c4a
+    field public static final int COMMAND_CODE_SESSION_SKIP_BACKWARD = 40003; // 0x9c43
+    field public static final int COMMAND_CODE_SESSION_SKIP_FORWARD = 40002; // 0x9c42
+    field public static final int COMMAND_CODE_VOLUME_ADJUST_VOLUME = 30001; // 0x7531
+    field public static final int COMMAND_CODE_VOLUME_SET_VOLUME = 30000; // 0x7530
+    field public static final int COMMAND_VERSION_1 = 1; // 0x1
+    field public static final int COMMAND_VERSION_2 = 2; // 0x2
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class SessionCommandGroup implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SessionCommandGroup();
+    ctor public SessionCommandGroup(java.util.Collection<androidx.media2.session.SessionCommand!>?);
+    method public java.util.Set<androidx.media2.session.SessionCommand!> getCommands();
+    method public boolean hasCommand(androidx.media2.session.SessionCommand);
+    method public boolean hasCommand(int);
+  }
+
+  public static final class SessionCommandGroup.Builder {
+    ctor public SessionCommandGroup.Builder();
+    ctor public SessionCommandGroup.Builder(androidx.media2.session.SessionCommandGroup);
+    method public androidx.media2.session.SessionCommandGroup.Builder addAllPredefinedCommands(int);
+    method public androidx.media2.session.SessionCommandGroup.Builder addCommand(androidx.media2.session.SessionCommand);
+    method public androidx.media2.session.SessionCommandGroup build();
+    method public androidx.media2.session.SessionCommandGroup.Builder removeCommand(androidx.media2.session.SessionCommand);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(isCustom=true) public class SessionResult extends androidx.versionedparcelable.CustomVersionedParcelable implements androidx.media2.common.BaseResult {
+    ctor public SessionResult(int, android.os.Bundle?);
+    method public long getCompletionTime();
+    method public android.os.Bundle? getCustomCommandResult();
+    method public androidx.media2.common.MediaItem? getMediaItem();
+    method public int getResultCode();
+    field public static final int RESULT_ERROR_SESSION_AUTHENTICATION_EXPIRED = -102; // 0xffffff9a
+    field public static final int RESULT_ERROR_SESSION_CONCURRENT_STREAM_LIMIT = -104; // 0xffffff98
+    field public static final int RESULT_ERROR_SESSION_DISCONNECTED = -100; // 0xffffff9c
+    field public static final int RESULT_ERROR_SESSION_NOT_AVAILABLE_IN_REGION = -106; // 0xffffff96
+    field public static final int RESULT_ERROR_SESSION_PARENTAL_CONTROL_RESTRICTED = -105; // 0xffffff97
+    field public static final int RESULT_ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED = -103; // 0xffffff99
+    field public static final int RESULT_ERROR_SESSION_SETUP_REQUIRED = -108; // 0xffffff94
+    field public static final int RESULT_ERROR_SESSION_SKIP_LIMIT_REACHED = -107; // 0xffffff95
+    field public static final int RESULT_SUCCESS = 0; // 0x0
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class SessionToken implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public SessionToken(android.content.Context, android.content.ComponentName);
+    method public android.os.Bundle getExtras();
+    method public String getPackageName();
+    method public String? getServiceName();
+    method public int getType();
+    method public int getUid();
+    field public static final int TYPE_LIBRARY_SERVICE = 2; // 0x2
+    field public static final int TYPE_SESSION = 0; // 0x0
+    field public static final int TYPE_SESSION_SERVICE = 1; // 0x1
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class StarRating implements androidx.media2.common.Rating {
+    ctor public StarRating(@IntRange(from=1) int);
+    ctor public StarRating(@IntRange(from=1) int, float);
+    method public int getMaxStars();
+    method public float getStarRating();
+    method public boolean isRated();
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize public final class ThumbRating implements androidx.media2.common.Rating {
+    ctor public ThumbRating();
+    ctor public ThumbRating(boolean);
+    method public boolean isRated();
+    method public boolean isThumbUp();
+  }
+
+}
+
diff --git a/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCallbackWithMediaControllerCompatTest.java b/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCallbackWithMediaControllerCompatTest.java
index 1fa48ff..1352b13 100644
--- a/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCallbackWithMediaControllerCompatTest.java
+++ b/media2/session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCallbackWithMediaControllerCompatTest.java
@@ -948,7 +948,7 @@
             sessionHandler.postAndSync(new Runnable() {
                 @Override
                 public void run() {
-                    mSession = new MediaSession.Builder(mContext, mPlayer)
+                    mSession = new MediaSession.Builder(mContext, player)
                             .setSessionCallback(sHandlerExecutor, new SessionCallback() {})
                             .setId("testDeadlock").build();
                 }
diff --git a/media2/session/version-compat-tests/previous/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCallbackWithMediaControllerCompatTest.java b/media2/session/version-compat-tests/previous/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCallbackWithMediaControllerCompatTest.java
index 443d768..883db40 100644
--- a/media2/session/version-compat-tests/previous/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCallbackWithMediaControllerCompatTest.java
+++ b/media2/session/version-compat-tests/previous/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCallbackWithMediaControllerCompatTest.java
@@ -856,7 +856,7 @@
             sessionHandler.postAndSync(new Runnable() {
                 @Override
                 public void run() {
-                    mSession = new MediaSession.Builder(mContext, mPlayer)
+                    mSession = new MediaSession.Builder(mContext, player)
                             .setSessionCallback(sHandlerExecutor, new SessionCallback() {})
                             .setId("testDeadlock").build();
                 }
diff --git a/media2/widget/api/1.1.0-beta01.txt b/media2/widget/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..b614f1a
--- /dev/null
+++ b/media2/widget/api/1.1.0-beta01.txt
@@ -0,0 +1,38 @@
+// Signature format: 3.0
+package androidx.media2.widget {
+
+  public class MediaControlView extends android.view.ViewGroup {
+    ctor public MediaControlView(android.content.Context);
+    ctor public MediaControlView(android.content.Context, android.util.AttributeSet?);
+    ctor public MediaControlView(android.content.Context, android.util.AttributeSet?, int);
+    method public void requestPlayButtonFocus();
+    method public void setMediaController(androidx.media2.session.MediaController);
+    method public void setOnFullScreenListener(androidx.media2.widget.MediaControlView.OnFullScreenListener?);
+    method public void setPlayer(androidx.media2.common.SessionPlayer);
+  }
+
+  public static interface MediaControlView.OnFullScreenListener {
+    method public void onFullScreen(android.view.View, boolean);
+  }
+
+  public class VideoView extends android.view.ViewGroup {
+    ctor public VideoView(android.content.Context);
+    ctor public VideoView(android.content.Context, android.util.AttributeSet?);
+    ctor public VideoView(android.content.Context, android.util.AttributeSet?, int);
+    method public androidx.media2.widget.MediaControlView? getMediaControlView();
+    method public int getViewType();
+    method public void setMediaControlView(androidx.media2.widget.MediaControlView, long);
+    method public void setMediaController(androidx.media2.session.MediaController);
+    method public void setOnViewTypeChangedListener(androidx.media2.widget.VideoView.OnViewTypeChangedListener?);
+    method public void setPlayer(androidx.media2.common.SessionPlayer);
+    method public void setViewType(int);
+    field public static final int VIEW_TYPE_SURFACEVIEW = 0; // 0x0
+    field public static final int VIEW_TYPE_TEXTUREVIEW = 1; // 0x1
+  }
+
+  public static interface VideoView.OnViewTypeChangedListener {
+    method public void onViewTypeChanged(android.view.View, int);
+  }
+
+}
+
diff --git a/media2/widget/api/public_plus_experimental_1.1.0-beta01.txt b/media2/widget/api/public_plus_experimental_1.1.0-beta01.txt
new file mode 100644
index 0000000..b614f1a
--- /dev/null
+++ b/media2/widget/api/public_plus_experimental_1.1.0-beta01.txt
@@ -0,0 +1,38 @@
+// Signature format: 3.0
+package androidx.media2.widget {
+
+  public class MediaControlView extends android.view.ViewGroup {
+    ctor public MediaControlView(android.content.Context);
+    ctor public MediaControlView(android.content.Context, android.util.AttributeSet?);
+    ctor public MediaControlView(android.content.Context, android.util.AttributeSet?, int);
+    method public void requestPlayButtonFocus();
+    method public void setMediaController(androidx.media2.session.MediaController);
+    method public void setOnFullScreenListener(androidx.media2.widget.MediaControlView.OnFullScreenListener?);
+    method public void setPlayer(androidx.media2.common.SessionPlayer);
+  }
+
+  public static interface MediaControlView.OnFullScreenListener {
+    method public void onFullScreen(android.view.View, boolean);
+  }
+
+  public class VideoView extends android.view.ViewGroup {
+    ctor public VideoView(android.content.Context);
+    ctor public VideoView(android.content.Context, android.util.AttributeSet?);
+    ctor public VideoView(android.content.Context, android.util.AttributeSet?, int);
+    method public androidx.media2.widget.MediaControlView? getMediaControlView();
+    method public int getViewType();
+    method public void setMediaControlView(androidx.media2.widget.MediaControlView, long);
+    method public void setMediaController(androidx.media2.session.MediaController);
+    method public void setOnViewTypeChangedListener(androidx.media2.widget.VideoView.OnViewTypeChangedListener?);
+    method public void setPlayer(androidx.media2.common.SessionPlayer);
+    method public void setViewType(int);
+    field public static final int VIEW_TYPE_SURFACEVIEW = 0; // 0x0
+    field public static final int VIEW_TYPE_TEXTUREVIEW = 1; // 0x1
+  }
+
+  public static interface VideoView.OnViewTypeChangedListener {
+    method public void onViewTypeChanged(android.view.View, int);
+  }
+
+}
+
diff --git a/media2/widget/api/res-1.1.0-beta01.txt b/media2/widget/api/res-1.1.0-beta01.txt
new file mode 100644
index 0000000..9015818
--- /dev/null
+++ b/media2/widget/api/res-1.1.0-beta01.txt
@@ -0,0 +1,2 @@
+attr enableControlView
+attr viewType
diff --git a/media2/widget/api/restricted_1.1.0-beta01.txt b/media2/widget/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..b614f1a
--- /dev/null
+++ b/media2/widget/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,38 @@
+// Signature format: 3.0
+package androidx.media2.widget {
+
+  public class MediaControlView extends android.view.ViewGroup {
+    ctor public MediaControlView(android.content.Context);
+    ctor public MediaControlView(android.content.Context, android.util.AttributeSet?);
+    ctor public MediaControlView(android.content.Context, android.util.AttributeSet?, int);
+    method public void requestPlayButtonFocus();
+    method public void setMediaController(androidx.media2.session.MediaController);
+    method public void setOnFullScreenListener(androidx.media2.widget.MediaControlView.OnFullScreenListener?);
+    method public void setPlayer(androidx.media2.common.SessionPlayer);
+  }
+
+  public static interface MediaControlView.OnFullScreenListener {
+    method public void onFullScreen(android.view.View, boolean);
+  }
+
+  public class VideoView extends android.view.ViewGroup {
+    ctor public VideoView(android.content.Context);
+    ctor public VideoView(android.content.Context, android.util.AttributeSet?);
+    ctor public VideoView(android.content.Context, android.util.AttributeSet?, int);
+    method public androidx.media2.widget.MediaControlView? getMediaControlView();
+    method public int getViewType();
+    method public void setMediaControlView(androidx.media2.widget.MediaControlView, long);
+    method public void setMediaController(androidx.media2.session.MediaController);
+    method public void setOnViewTypeChangedListener(androidx.media2.widget.VideoView.OnViewTypeChangedListener?);
+    method public void setPlayer(androidx.media2.common.SessionPlayer);
+    method public void setViewType(int);
+    field public static final int VIEW_TYPE_SURFACEVIEW = 0; // 0x0
+    field public static final int VIEW_TYPE_TEXTUREVIEW = 1; // 0x1
+  }
+
+  public static interface VideoView.OnViewTypeChangedListener {
+    method public void onViewTypeChanged(android.view.View, int);
+  }
+
+}
+
diff --git a/mediarouter/mediarouter/build.gradle b/mediarouter/mediarouter/build.gradle
index 14516e3..2015482 100644
--- a/mediarouter/mediarouter/build.gradle
+++ b/mediarouter/mediarouter/build.gradle
@@ -24,12 +24,10 @@
 }
 
 dependencies {
-    api("androidx.media:media:1.2.0-beta01")
+    api("androidx.media:media:1.2.0-rc01")
     api(GUAVA_LISTENABLE_FUTURE)
 
     implementation("androidx.appcompat:appcompat:1.1.0")
-    // TODO: remove core:1.3.0 once media:1.2.0-alpha05 or beta01 is released.
-    implementation("androidx.core:core:1.3.0")
     implementation("androidx.palette:palette:1.0.0")
     implementation("androidx.recyclerview:recyclerview:1.1.0")
 
diff --git a/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt b/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
index 146ab24..1c26321 100644
--- a/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
+++ b/paging/common/src/main/kotlin/androidx/paging/LegacyPagingSource.kt
@@ -33,9 +33,20 @@
     private val fetchDispatcher: CoroutineDispatcher = DirectDispatcher,
     internal val dataSourceFactory: () -> DataSource<Key, Value>
 ) : PagingSource<Key, Value>() {
+    // Lazily initialize because it must be created on fetchDispatcher, but PagingSourceFactory
+    // passed to Pager is a non-suspending method.
     internal val dataSource by lazy {
-        dataSourceFactory().also {
-            it.addInvalidatedCallback { invalidate() }
+        dataSourceFactory().also { dataSource ->
+            dataSource.addInvalidatedCallback(::invalidate)
+            // LegacyPagingSource registers invalidate callback after DataSource is created, so we
+            // need to check for race condition here. If DataSource is already invalid, simply
+            // propagate invalidation manually.
+            if (dataSource.isInvalid && !invalid) {
+                dataSource.removeInvalidatedCallback(::invalidate)
+                // Note: Calling this.invalidate directly will re-evaluate dataSource's by lazy
+                // init block, since we haven't returned a value for dataSource yet.
+                super.invalidate()
+            }
         }
     }
 
diff --git a/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt b/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
index 4383e6f..7e490dd 100644
--- a/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
+++ b/paging/common/src/test/kotlin/androidx/paging/LegacyPagingSourceTest.kt
@@ -251,6 +251,43 @@
         assertTrue { initialized }
     }
 
+    @Test
+    fun dataSourceInvalidateBeforePagingSourceInvalidateCallbackAdded() {
+        val dataSourceFactory = object : DataSource.Factory<Int, String>() {
+            val dataSources = mutableListOf<DataSource<Int, String>>()
+            var i = 0
+
+            override fun create(): DataSource<Int, String> {
+                return when (i++) {
+                    0 -> createTestPositionalDataSource().apply {
+                        // Invalidate before we give LegacyPagingSource a chance to register
+                        // invalidate callback.
+                        invalidate()
+                    }
+                    else -> createTestPositionalDataSource()
+                }.also { dataSources.add(it) }
+            }
+        }
+
+        val pagingSourceFactory = dataSourceFactory.asPagingSourceFactory().let {
+            { it() as LegacyPagingSource }
+        }
+
+        val pagingSource0 = pagingSourceFactory()
+        assertTrue { pagingSource0.dataSource.isInvalid }
+        assertTrue { pagingSource0.invalid }
+        assertTrue { dataSourceFactory.dataSources[0].isInvalid }
+        assertEquals(dataSourceFactory.dataSources[0], pagingSource0.dataSource)
+
+        val pagingSource1 = pagingSourceFactory()
+        assertFalse { pagingSource1.dataSource.isInvalid }
+        assertFalse { pagingSource1.invalid }
+        assertFalse { dataSourceFactory.dataSources[1].isInvalid }
+        assertEquals(dataSourceFactory.dataSources[1], pagingSource1.dataSource)
+
+        assertEquals(2, dataSourceFactory.dataSources.size)
+    }
+
     @Suppress("DEPRECATION")
     private fun createTestPositionalDataSource(expectInitialLoad: Boolean = false) =
         object : PositionalDataSource<String>() {
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DisallowInterceptFilterTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DisallowInterceptFilterTest.java
new file mode 100644
index 0000000..ee5316e
--- /dev/null
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/DisallowInterceptFilterTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.recyclerview.selection;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.recyclerview.selection.testing.TestEvents;
+import androidx.recyclerview.selection.testing.TestOnItemTouchListener;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DisallowInterceptFilterTest {
+
+    private TestOnItemTouchListener mListener;
+    private DisallowInterceptFilter mFilter;
+
+    @Before
+    public void setUp() {
+        mListener = new TestOnItemTouchListener();
+        mFilter = new DisallowInterceptFilter(mListener);
+    }
+
+    @Test
+    public void testForwardsEventsToDelegate() {
+        mFilter.onInterceptTouchEvent(null, TestEvents.Touch.DOWN);
+        mListener.assertOnInterceptTouchEventCalled(1);
+    }
+
+    @Test
+    public void testReflectsDelegateReturnValue() {
+        assertFalse(mFilter.onInterceptTouchEvent(null, TestEvents.Touch.UP));
+        mListener.consumeEvents(true);
+        assertTrue(mFilter.onInterceptTouchEvent(null, TestEvents.Touch.MOVE));
+    }
+
+    @Test
+    public void testRespectsDisallowRequest() {
+        mFilter.onRequestDisallowInterceptTouchEvent(true);
+        mFilter.onInterceptTouchEvent(null, TestEvents.Touch.UP);
+        mListener.assertOnInterceptTouchEventCalled(0);
+    }
+
+    @Test
+    public void testResetsDisallowOnDown() {
+        mFilter.onRequestDisallowInterceptTouchEvent(true);
+        mFilter.onInterceptTouchEvent(null, TestEvents.Touch.DOWN);
+        mListener.assertOnInterceptTouchEventCalled(1);
+    }
+
+    @Test
+    public void testIsResettable() {
+        assertFalse(mFilter.isResetRequired());
+        mFilter.onRequestDisallowInterceptTouchEvent(true);
+        assertTrue(mFilter.isResetRequired());
+        mFilter.reset();
+        assertFalse(mFilter.isResetRequired());
+    }
+
+    @Test
+    public void testResetClearsDisallowBit() {
+        mFilter.onRequestDisallowInterceptTouchEvent(true);
+        mFilter.reset();
+        mFilter.onInterceptTouchEvent(null, TestEvents.Touch.MOVE);
+        mListener.assertOnInterceptTouchEventCalled(1);
+    }
+}
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/EventBackstopTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/EventBackstopTest.java
new file mode 100644
index 0000000..0c159ec
--- /dev/null
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/EventBackstopTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.recyclerview.selection;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.recyclerview.selection.testing.TestEvents;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class EventBackstopTest {
+
+    private EventBackstop mBackstop;
+
+    @Before
+    public void setUp() {
+        mBackstop = new EventBackstop();
+    }
+
+    @Test
+    public void testConsumesOneUpAfterLongPress() {
+        mBackstop.onLongPress();
+        assertTrue(mBackstop.onInterceptTouchEvent(null, TestEvents.Touch.UP));
+    }
+
+    @Test
+    public void testIgnoresUpActionsAfterFirst() {
+        mBackstop.onLongPress();
+        mBackstop.onInterceptTouchEvent(null, TestEvents.Touch.UP);
+        assertFalse(mBackstop.onInterceptTouchEvent(null, TestEvents.Touch.UP));
+    }
+
+    @Test
+    public void testIgnoresUpWithoutLongPress() {
+        assertFalse(mBackstop.onInterceptTouchEvent(null, TestEvents.Touch.UP));
+    }
+
+    @Test
+    public void testIgnoresOtherActionsAfterLongPress() {
+        mBackstop.onLongPress();
+        assertFalse(mBackstop.onInterceptTouchEvent(null, TestEvents.Touch.DOWN));
+        assertFalse(mBackstop.onInterceptTouchEvent(null, TestEvents.Touch.MOVE));
+    }
+}
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/EventRouterTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/EventRouterTest.java
index 1d7c911..b3a01ff 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/EventRouterTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/EventRouterTest.java
@@ -16,16 +16,14 @@
 
 package androidx.recyclerview.selection;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.view.MotionEvent;
 
-import androidx.annotation.NonNull;
 import androidx.recyclerview.selection.testing.TestEvents;
+import androidx.recyclerview.selection.testing.TestOnItemTouchListener;
 import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -34,9 +32,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
-import java.util.List;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class EventRouterTest {
@@ -98,33 +93,4 @@
 
         assertFalse(mRouter.isResetRequired());
     }
-
-    private static final class TestOnItemTouchListener implements OnItemTouchListener {
-
-        private List<MotionEvent> mOnInterceptTouchEventCalls = new ArrayList<>();
-        private List<MotionEvent> mOnTouchEventCalls = new ArrayList<>();
-
-        @Override
-        public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
-            mOnInterceptTouchEventCalls.add(e);
-            return false;
-        }
-
-        @Override
-        public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
-            mOnTouchEventCalls.add(e);
-        }
-
-        @Override
-        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        }
-
-        void assertOnInterceptTouchEventCalled(int expectedTimesCalled) {
-            assertEquals(expectedTimesCalled, mOnInterceptTouchEventCalls.size());
-        }
-
-        void assertOnTouchEventCalled(int expectedTimesCalled) {
-            assertEquals(expectedTimesCalled, mOnTouchEventCalls.size());
-        }
-    }
 }
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/TouchInputHandlerTest.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/TouchInputHandlerTest.java
index 3065df1..7444d12 100644
--- a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/TouchInputHandlerTest.java
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/TouchInputHandlerTest.java
@@ -55,6 +55,7 @@
     private TestItemDetailsLookup mDetailsLookup;
     private SelectionProbe mSelection;
     private TestDragListener mDragInitiatedListener;
+    private TestRunnable mLongPressCallback;
 
     @Before
     public void setUp() {
@@ -66,6 +67,7 @@
         mHapticPerformer = new TestRunnable();
         mActivationCallbacks = new TestOnItemActivatedListener<>();
         mDragInitiatedListener = new TestDragListener();
+        mLongPressCallback = new TestRunnable();
 
         mInputDelegate = new TouchInputHandler<>(
                 mSelectionMgr,
@@ -78,7 +80,8 @@
                 mDragInitiatedListener,
                 mActivationCallbacks,
                 new TestFocusDelegate<>(),
-                mHapticPerformer);
+                mHapticPerformer,
+                mLongPressCallback::run);
     }
 
     @Test
@@ -107,6 +110,16 @@
         mSelection.assertSelection(7);
     }
 
+
+    @Test
+    public void testLongPress_NotifiesLongPressCallback() {
+        mSelectionPredicate.setReturnValue(true);
+        mDetailsLookup.initAt(7);
+
+        mInputDelegate.onLongPress(DOWN);
+        mLongPressCallback.assertRun();
+    }
+
     @Test
     public void testLongPress_UnselectedItem_PerformsHapticFeedback() {
         mSelectionPredicate.setReturnValue(true);
diff --git a/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/testing/TestOnItemTouchListener.java b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/testing/TestOnItemTouchListener.java
new file mode 100644
index 0000000..1f2e8f9
--- /dev/null
+++ b/recyclerview/recyclerview-selection/src/androidTest/java/androidx/recyclerview/selection/testing/TestOnItemTouchListener.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.recyclerview.selection.testing;
+
+import static org.junit.Assert.assertEquals;
+
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.selection.Resettable;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class TestOnItemTouchListener implements OnItemTouchListener, Resettable {
+
+    private final List<MotionEvent> mOnInterceptTouchEventCalls = new ArrayList<>();
+    private final List<MotionEvent> mOnTouchEventCalls = new ArrayList<>();
+    private boolean mConsumeEvents;
+
+    @Override
+    public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
+        mOnInterceptTouchEventCalls.add(e);
+        return mConsumeEvents;
+    }
+
+    @Override
+    public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
+        mOnTouchEventCalls.add(e);
+    }
+
+    @Override
+    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+    }
+
+    public void consumeEvents(boolean enabled) {
+        mConsumeEvents = enabled;
+    }
+
+    public void assertOnInterceptTouchEventCalled(int expectedTimesCalled) {
+        assertEquals(expectedTimesCalled, mOnInterceptTouchEventCalls.size());
+    }
+
+    public void assertOnTouchEventCalled(int expectedTimesCalled) {
+        assertEquals(expectedTimesCalled, mOnTouchEventCalls.size());
+    }
+
+    @Override
+    public boolean isResetRequired() {
+        return !mOnInterceptTouchEventCalls.isEmpty()
+                || !mOnTouchEventCalls.isEmpty()
+                || mConsumeEvents;
+    }
+
+    @Override
+    public void reset() {
+        mOnInterceptTouchEventCalls.clear();
+        mOnTouchEventCalls.clear();
+        mConsumeEvents = false;
+    }
+}
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/DisallowInterceptFilter.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/DisallowInterceptFilter.java
new file mode 100644
index 0000000..6c60805
--- /dev/null
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/DisallowInterceptFilter.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.recyclerview.selection;
+
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
+
+/**
+ * Wrapper class that regulates delivery of MotionEvents to delegate listeners, uniformly
+ * honoring requests to onRequestDisallowInterceptTouchEvent.
+ * Wrap this class around other OnItemTouchListeners to bestow them with
+ * proper support for onRequestDisallowInterceptTouchEvent.
+ */
+// TODO: Replace in-situ "disallow" implementation in EventRouter, ResetManager,
+//  BandSelectionHelper, GestureSelectionHelper by wrapping w/ this class.
+class DisallowInterceptFilter implements
+        OnItemTouchListener, Resettable {
+
+    private final OnItemTouchListener mDelegate;
+    private boolean mDisallowIntercept;
+
+    DisallowInterceptFilter(@NonNull OnItemTouchListener delegate) {
+        mDelegate = delegate;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
+        // Reset disallow when the event is down as advised in http://b/139141511#comment20.
+        if (mDisallowIntercept && MotionEvents.isActionDown(e)) {
+            mDisallowIntercept = false;
+        }
+        return !mDisallowIntercept && mDelegate.onInterceptTouchEvent(rv, e);
+    }
+
+    @Override
+    public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
+        mDelegate.onInterceptTouchEvent(rv, e);
+    }
+
+    @Override
+    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        mDisallowIntercept = true;
+    }
+
+    @Override
+    public boolean isResetRequired() {
+        return mDisallowIntercept;
+    }
+
+    @Override
+    public void reset() {
+        mDisallowIntercept = false;
+    }
+}
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventBackstop.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventBackstop.java
new file mode 100644
index 0000000..c56daa7
--- /dev/null
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventBackstop.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.recyclerview.selection;
+
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
+
+/**
+ * OnItemTouchListener that claims all ACTION_UP events in streams that have otherwise gone
+ * unclaimed after a LongPress has been detected by GestureDetector.
+ * This addresses issue described in b/166836317, where child view
+ * OnClickListeners were being called unexpectedly.
+ */
+class EventBackstop implements OnItemTouchListener, Resettable {
+
+    private boolean mLongPressFired;
+
+    @Override
+    public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
+        // We only claim ACTION_UP events after a LongPress event. Were we to claim
+        // all ACTION_UP events we'd deprive RecyclerView of the signal it needs to
+        // initiate fling scrolling.
+        if (MotionEvents.isActionUp(e) && mLongPressFired) {
+            mLongPressFired = false;
+            return true;
+        }
+
+        // Recover from disallow state.
+        if (MotionEvents.isActionDown(e) && isResetRequired()) {
+            reset();
+        }
+        return false;
+    }
+
+    @Override
+    public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
+        // We should never receive any events, but were we to...we want to ignore them.
+    }
+
+    @Override
+    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        throw new UnsupportedOperationException("Wrap me in an InterceptFilter.");
+    }
+
+    @Override
+    public boolean isResetRequired() {
+        return  mLongPressFired;
+    }
+
+    @Override
+    public void reset() {
+        mLongPressFired = false;
+    }
+
+    void onLongPress() {
+        mLongPressFired = true;
+    }
+}
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionTracker.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionTracker.java
index 89df922..fa2f1f8 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionTracker.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/SelectionTracker.java
@@ -744,9 +744,18 @@
             GestureDetectorWrapper gestureDetectorWrapper =
                     new GestureDetectorWrapper(gestureDetector);
 
+            // Temp fix for b/166836317.
+            // TODO: Add support for multiple listeners per tool type to EventRouter, then
+            //  register backstop with primary router.
+            EventRouter backstopRouter = new EventRouter();
+            EventBackstop backstop = new EventBackstop();
+            DisallowInterceptFilter backstopWrapper = new DisallowInterceptFilter(backstop);
+            backstopRouter.set(MotionEvent.TOOL_TYPE_FINGER, backstopWrapper);
+
             // Finally hook the framework up to listening to RecycleView events.
             mRecyclerView.addOnItemTouchListener(eventRouter);
             mRecyclerView.addOnItemTouchListener(gestureDetectorWrapper);
+            mRecyclerView.addOnItemTouchListener(backstopRouter);
 
             // Reset manager listens for cancel events from RecyclerView. In response to that it
             // advises other classes it is time to reset state.
@@ -773,6 +782,9 @@
             resetMgr.addResetHandler(gestureSelectionHelper);
             resetMgr.addResetHandler(gestureDetectorWrapper);
             resetMgr.addResetHandler(eventRouter);
+            resetMgr.addResetHandler(backstopRouter);
+            resetMgr.addResetHandler(backstop);
+            resetMgr.addResetHandler(backstopWrapper);
 
             // But before you move on, there's more work to do. Event plumbing has been
             // installed, but we haven't registered any of our helpers or callbacks.
@@ -820,14 +832,7 @@
                     mKeyProvider,
                     mDetailsLookup,
                     mSelectionPredicate,
-                    new Runnable() {
-                        @Override
-                        public void run() {
-                            if (mSelectionPredicate.canSelectMultiple()) {
-                                gestureSelectionHelper.start();
-                            }
-                        }
-                    },
+                    gestureSelectionHelper::start,
                     mOnDragInitiatedListener,
                     mOnItemActivatedListener,
                     mFocusDelegate,
@@ -836,7 +841,9 @@
                         public void run() {
                             mRecyclerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
                         }
-                    });
+                    },
+                    // Provide temporary glue to address b/166836317
+                    backstop::onLongPress);
 
             for (int toolType : mGestureToolTypes) {
                 gestureRouter.register(toolType, touchHandler);
diff --git a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/TouchInputHandler.java b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/TouchInputHandler.java
index 5f8d327..5a10446 100644
--- a/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/TouchInputHandler.java
+++ b/recyclerview/recyclerview-selection/src/main/java/androidx/recyclerview/selection/TouchInputHandler.java
@@ -45,6 +45,7 @@
     private final OnDragInitiatedListener mOnDragInitiatedListener;
     private final Runnable mGestureStarter;
     private final Runnable mHapticPerformer;
+    private final Runnable mLongPressCallback;
 
     TouchInputHandler(
             @NonNull SelectionTracker<K> selectionTracker,
@@ -55,7 +56,8 @@
             @NonNull OnDragInitiatedListener onDragInitiatedListener,
             @NonNull OnItemActivatedListener<K> onItemActivatedListener,
             @NonNull FocusDelegate<K> focusDelegate,
-            @NonNull Runnable hapticPerformer) {
+            @NonNull Runnable hapticPerformer,
+            @NonNull Runnable longPressCallback) {
 
         super(selectionTracker, keyProvider, focusDelegate);
 
@@ -72,6 +74,7 @@
         mOnItemActivatedListener = onItemActivatedListener;
         mOnDragInitiatedListener = onDragInitiatedListener;
         mHapticPerformer = hapticPerformer;
+        mLongPressCallback = longPressCallback;
     }
 
     @Override
@@ -143,6 +146,9 @@
             return;
         }
 
+        // Temprary fix to address b/166836317.
+        mLongPressCallback.run();
+
         if (shouldExtendRange(e)) {
             extendSelectionRange(item);
             mHapticPerformer.run();
diff --git a/samples/Support7Demos/src/main/AndroidManifest.xml b/samples/Support7Demos/src/main/AndroidManifest.xml
index 94f76a2..1381479 100644
--- a/samples/Support7Demos/src/main/AndroidManifest.xml
+++ b/samples/Support7Demos/src/main/AndroidManifest.xml
@@ -277,6 +277,15 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".app.AppCompatWidgetsIcons"
+            android:label="@string/appcompat_widgets_icons"
+            android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.android.supportv7.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".app.AppCompatWidgetsRatingBars"
             android:label="@string/appcompat_widgets_ratingbars"
             android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/app/AppCompatWidgetsIcons.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/app/AppCompatWidgetsIcons.java
new file mode 100644
index 0000000..e7446ce
--- /dev/null
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/app/AppCompatWidgetsIcons.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.supportv7.app;
+
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.example.android.supportv7.R;
+
+/**
+ * This demonstrates the styled {@link android.widget.Button} widgets in AppCompat.
+ */
+public class AppCompatWidgetsIcons extends AppCompatActivity {
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.appcompat_widgets_icons);
+    }
+
+}
diff --git a/samples/Support7Demos/src/main/res/layout/appcompat_widgets_icons.xml b/samples/Support7Demos/src/main/res/layout/appcompat_widgets_icons.xml
new file mode 100644
index 0000000..1eb3b45
--- /dev/null
+++ b/samples/Support7Demos/src/main/res/layout/appcompat_widgets_icons.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:padding="16dp">
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:srcCompat="?attr/actionModeCutDrawable" />
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:srcCompat="?attr/actionModeCopyDrawable" />
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:srcCompat="?attr/actionModePasteDrawable" />
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:srcCompat="?attr/actionModeSelectAllDrawable" />
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:srcCompat="?attr/actionModeShareDrawable" />
+
+    </LinearLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/samples/Support7Demos/src/main/res/values/strings.xml b/samples/Support7Demos/src/main/res/values/strings.xml
index cd873f5..e067d47 100644
--- a/samples/Support7Demos/src/main/res/values/strings.xml
+++ b/samples/Support7Demos/src/main/res/values/strings.xml
@@ -78,6 +78,7 @@
     <string name="action_bar_action_mode">AppCompat/Action Bar/Action Mode</string>
     <string name="action_bar_hide_scroll">AppCompat/Action Bar/Hide on Scroll</string>
     <string name="appcompat_widgets_buttons">AppCompat/Widgets/Buttons</string>
+    <string name="appcompat_widgets_icons">AppCompat/Widgets/Icons</string>
     <string name="appcompat_widgets_ratingbars">AppCompat/Widgets/Rating Bars</string>
     <string name="appcompat_widgets_spinners">AppCompat/Widgets/Spinners</string>
     <string name="appcompat_widgets_switches">AppCompat/Widgets/Switches</string>
diff --git a/wear/wear-complications-rendering/src/test/java/androidx/wear/complications/rendering/ComplicationDrawableTest.java b/wear/wear-complications-rendering/src/test/java/androidx/wear/complications/rendering/ComplicationDrawableTest.java
index 1d4ac84..0da01ad 100644
--- a/wear/wear-complications-rendering/src/test/java/androidx/wear/complications/rendering/ComplicationDrawableTest.java
+++ b/wear/wear-complications-rendering/src/test/java/androidx/wear/complications/rendering/ComplicationDrawableTest.java
@@ -717,7 +717,7 @@
                         }
 
                         @Override
-                        public void onDrawInternal$wear_watchface_debug(
+                        public void renderInternal$wear_watchface_debug(
                                 @NotNull Calendar calendar) {
                         }
                     },
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasWatchFaceService.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasWatchFaceService.kt
index 1e5f975..4ac16c6 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasWatchFaceService.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasWatchFaceService.kt
@@ -34,12 +34,11 @@
 import androidx.wear.watchface.Complication
 import androidx.wear.watchface.ComplicationsHolder
 import androidx.wear.watchface.DrawMode
-import androidx.wear.watchface.UnitSquareBoundsProvider
-import androidx.wear.watchface.WatchFaceHost
-import androidx.wear.watchface.WatchState
 import androidx.wear.watchface.WatchFace
+import androidx.wear.watchface.WatchFaceHost
 import androidx.wear.watchface.WatchFaceService
 import androidx.wear.watchface.WatchFaceType
+import androidx.wear.watchface.WatchState
 import androidx.wear.watchface.style.BooleanUserStyleCategory
 import androidx.wear.watchface.style.DoubleRangeUserStyleCategory
 import androidx.wear.watchface.style.ListUserStyleCategory
@@ -114,9 +113,8 @@
         )
         val complicationSlots = ComplicationsHolder(
             listOf(
-                Complication(
+                Complication.Builder(
                     EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID,
-                    UnitSquareBoundsProvider(RectF(0.2f, 0.4f, 0.4f, 0.6f)),
                     watchFaceStyle.getComplicationDrawableRenderer(this, watchState),
                     intArrayOf(
                         ComplicationData.TYPE_RANGED_VALUE,
@@ -125,12 +123,12 @@
                         ComplicationData.TYPE_ICON,
                         ComplicationData.TYPE_SMALL_IMAGE
                     ),
-                    Complication.DefaultComplicationProvider(SystemProviders.DAY_OF_WEEK),
-                    ComplicationData.TYPE_SHORT_TEXT
-                ),
-                Complication(
+                    Complication.DefaultComplicationProvider(SystemProviders.DAY_OF_WEEK)
+                ).setUnitSquareBounds(RectF(0.2f, 0.4f, 0.4f, 0.6f))
+                    .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                    .build(),
+                Complication.Builder(
                     EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID,
-                    UnitSquareBoundsProvider(RectF(0.6f, 0.4f, 0.8f, 0.6f)),
                     watchFaceStyle.getComplicationDrawableRenderer(this, watchState),
                     intArrayOf(
                         ComplicationData.TYPE_RANGED_VALUE,
@@ -139,9 +137,10 @@
                         ComplicationData.TYPE_ICON,
                         ComplicationData.TYPE_SMALL_IMAGE
                     ),
-                    Complication.DefaultComplicationProvider(SystemProviders.STEP_COUNT),
-                    ComplicationData.TYPE_SHORT_TEXT
-                )
+                    Complication.DefaultComplicationProvider(SystemProviders.STEP_COUNT)
+                ).setUnitSquareBounds(RectF(0.6f, 0.4f, 0.8f, 0.6f))
+                    .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                    .build()
             )
         )
         val renderer = ExampleCanvasRenderer(
@@ -238,9 +237,8 @@
                     // Apply the userStyle to the complications. ComplicationDrawables for each of the
                     // styles are defined in XML so we need to replace the complication's drawables.
                     for ((_, complication) in complicationsHolder.complications) {
-                        complication.setRenderer(
+                        complication.renderer =
                             watchFaceColorStyle.getComplicationDrawableRenderer(context, watchState)
-                        )
                     }
 
                     val drawPipsOption =
@@ -256,7 +254,7 @@
         )
     }
 
-    override fun onDraw(canvas: Canvas, bounds: Rect, calendar: Calendar) {
+    override fun render(canvas: Canvas, bounds: Rect, calendar: Calendar) {
         val style = if (drawMode == DrawMode.AMBIENT) {
             watchFaceColorStyle.ambientStyle
         } else {
@@ -466,7 +464,7 @@
             if (drawMode == DrawMode.COMPLICATION_SELECT) {
                 drawComplicationSelectDashBorders(
                     canvas,
-                    complication.boundsProvider.computeBounds(complication, screen)
+                    complication.computeBounds(screen)
                 )
             }
         }
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
index 5891e22..499c6de 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
@@ -437,7 +437,7 @@
         }
     }
 
-    override fun onDraw(calendar: Calendar) {
+    override fun render(calendar: Calendar) {
         // Draw background color and select the appropriate view projection matrix. The background
         // should always be black in ambient mode. The view projection matrix used is overhead in
         // ambient. In interactive mode, it's tilted depending on the current time.
diff --git a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
index 8414197..813b5b4 100644
--- a/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
+++ b/wear/wear-watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
@@ -32,12 +32,11 @@
 import androidx.wear.watchface.Complication
 import androidx.wear.watchface.ComplicationDrawableRenderer
 import androidx.wear.watchface.ComplicationsHolder
-import androidx.wear.watchface.UnitSquareBoundsProvider
-import androidx.wear.watchface.WatchFaceHost
-import androidx.wear.watchface.WatchState
 import androidx.wear.watchface.WatchFace
+import androidx.wear.watchface.WatchFaceHost
 import androidx.wear.watchface.WatchFaceService
 import androidx.wear.watchface.WatchFaceType
+import androidx.wear.watchface.WatchState
 import androidx.wear.watchface.style.ListUserStyleCategory
 import androidx.wear.watchface.style.UserStyleCategory
 import androidx.wear.watchface.style.UserStyleManager
@@ -99,9 +98,8 @@
             )
             val complicationSlots = ComplicationsHolder(
                 listOf(
-                    Complication(
+                    Complication.Builder(
                         /*id */ 0,
-                        UnitSquareBoundsProvider(RectF(0.15625f, 0.1875f, 0.84375f, 0.3125f)),
                         ComplicationDrawableRenderer(
                             ComplicationDrawable(this),
                             watchState
@@ -113,14 +111,12 @@
                             ComplicationData.TYPE_ICON,
                             ComplicationData.TYPE_SMALL_IMAGE
                         ),
-                        Complication.DefaultComplicationProvider(SystemProviders.DAY_OF_WEEK),
-                        ComplicationData.TYPE_SHORT_TEXT
-                    ),
-                    Complication(
+                        Complication.DefaultComplicationProvider(SystemProviders.DAY_OF_WEEK)
+                    ).setUnitSquareBounds(RectF(0.15625f, 0.1875f, 0.84375f, 0.3125f))
+                        .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                        .build(),
+                    Complication.Builder(
                         /*id */ 1,
-                        UnitSquareBoundsProvider(
-                            RectF(0.1f, 0.5625f, 0.35f, 0.8125f)
-                        ),
                         ComplicationDrawableRenderer(
                             ComplicationDrawable(this),
                             watchState
@@ -132,9 +128,10 @@
                             ComplicationData.TYPE_ICON,
                             ComplicationData.TYPE_SMALL_IMAGE
                         ),
-                        Complication.DefaultComplicationProvider(SystemProviders.STEP_COUNT),
-                        ComplicationData.TYPE_SHORT_TEXT
-                    )
+                        Complication.DefaultComplicationProvider(SystemProviders.STEP_COUNT)
+                    ).setUnitSquareBounds(RectF(0.1f, 0.5625f, 0.35f, 0.8125f))
+                        .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                        .build()
                 )
             )
 
@@ -155,7 +152,7 @@
                     })
                 }
 
-                override fun onDraw(
+                override fun render(
                     canvas: Canvas,
                     bounds: Rect,
                     calendar: Calendar
diff --git a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasWatchFaceService.kt b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasWatchFaceService.kt
index 54af2af..3ec41bd 100644
--- a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasWatchFaceService.kt
+++ b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/TestCanvasWatchFaceService.kt
@@ -26,20 +26,19 @@
 import androidx.wear.watchface.Complication
 import androidx.wear.watchface.ComplicationsHolder
 import androidx.wear.watchface.NoInvalidateWatchFaceHostApi
-import androidx.wear.watchface.UnitSquareBoundsProvider
-import androidx.wear.watchface.WatchFaceHost
-import androidx.wear.watchface.WatchState
 import androidx.wear.watchface.WatchFace
+import androidx.wear.watchface.WatchFaceHost
 import androidx.wear.watchface.WatchFaceService
 import androidx.wear.watchface.WatchFaceType
+import androidx.wear.watchface.WatchState
 import androidx.wear.watchface.samples.EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID
 import androidx.wear.watchface.samples.EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID
 import androidx.wear.watchface.samples.ExampleCanvasRenderer
 import androidx.wear.watchface.samples.R
 import androidx.wear.watchface.samples.WatchFaceColorStyle
 import androidx.wear.watchface.style.BooleanUserStyleCategory
-import androidx.wear.watchface.style.ListUserStyleCategory
 import androidx.wear.watchface.style.DoubleRangeUserStyleCategory
+import androidx.wear.watchface.style.ListUserStyleCategory
 import androidx.wear.watchface.style.UserStyleManager
 
 /** A simple canvas test watch face for integration tests. */
@@ -103,9 +102,8 @@
         )
         val complicationSlots = ComplicationsHolder(
             listOf(
-                Complication(
+                Complication.Builder(
                     EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID,
-                    UnitSquareBoundsProvider(RectF(0.2f, 0.4f, 0.4f, 0.6f)),
                     watchFaceStyle.getComplicationDrawableRenderer(this, watchState),
                     intArrayOf(
                         ComplicationData.TYPE_RANGED_VALUE,
@@ -114,12 +112,12 @@
                         ComplicationData.TYPE_ICON,
                         ComplicationData.TYPE_SMALL_IMAGE
                     ),
-                    Complication.DefaultComplicationProvider(SystemProviders.DAY_OF_WEEK),
-                    ComplicationData.TYPE_SHORT_TEXT
-                ),
-                Complication(
+                    Complication.DefaultComplicationProvider(SystemProviders.DAY_OF_WEEK)
+                ).setUnitSquareBounds(RectF(0.2f, 0.4f, 0.4f, 0.6f))
+                    .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                    .build(),
+                Complication.Builder(
                     EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID,
-                    UnitSquareBoundsProvider(RectF(0.6f, 0.4f, 0.8f, 0.6f)),
                     watchFaceStyle.getComplicationDrawableRenderer(this, watchState),
                     intArrayOf(
                         ComplicationData.TYPE_RANGED_VALUE,
@@ -128,9 +126,10 @@
                         ComplicationData.TYPE_ICON,
                         ComplicationData.TYPE_SMALL_IMAGE
                     ),
-                    Complication.DefaultComplicationProvider(SystemProviders.STEP_COUNT),
-                    ComplicationData.TYPE_SHORT_TEXT
-                )
+                    Complication.DefaultComplicationProvider(SystemProviders.STEP_COUNT)
+                ).setUnitSquareBounds(RectF(0.6f, 0.4f, 0.8f, 0.6f))
+                    .setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+                    .build()
             )
         )
         val renderer = ExampleCanvasRenderer(
diff --git a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
index 645071a..3c2d61f 100644
--- a/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
+++ b/wear/wear-watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
@@ -46,7 +46,6 @@
 import androidx.wear.watchface.WatchFaceService
 import androidx.wear.watchface.samples.EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -267,7 +266,6 @@
     }
 
     @Test
-    @Ignore("Needs fixed image")
     fun testAmbientScreenshot() {
         handler.post {
             initCanvasWatchFace()
@@ -276,7 +274,7 @@
         }
 
         renderDoneLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)
-        bitmap.assertAgainstGolden(screenshotRule, "ambient_screenshot")
+        bitmap.assertAgainstGolden(screenshotRule, "ambient_screenshot2")
     }
 
     @Test
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/CanvasRenderer.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/CanvasRenderer.kt
index fe3ea1d..caf9661 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/CanvasRenderer.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/CanvasRenderer.kt
@@ -21,7 +21,6 @@
 import android.graphics.Color
 import android.graphics.Rect
 import android.icu.util.Calendar
-import android.util.Log
 import android.view.SurfaceHolder
 import androidx.annotation.IntDef
 import androidx.annotation.UiThread
@@ -61,25 +60,18 @@
     /** The type of canvas to use. */
     @CanvasType private val canvasType: Int
 ) : Renderer(surfaceHolder, userStyleManager, watchState) {
-    private companion object {
-        private const val TAG = "CanvasRenderer"
-    }
 
-    internal override fun onDrawInternal(
+    internal override fun renderInternal(
         calendar: Calendar
     ) {
-        val canvas = if (canvasType == CanvasType.HARDWARE) {
+        val canvas = (if (canvasType == CanvasType.HARDWARE) {
             surfaceHolder.lockHardwareCanvas()
         } else {
             surfaceHolder.lockCanvas()
-        }
-        if (canvas == null) {
-            Log.e(TAG, "Null canvas returned when locking the SurfaceHolder.")
-            return
-        }
+        }) ?: return
         try {
             if (watchState.isVisible) {
-                onDraw(canvas, surfaceHolder.surfaceFrame, calendar)
+                render(canvas, surfaceHolder.surfaceFrame, calendar)
             } else {
                 canvas.drawColor(Color.BLACK)
             }
@@ -100,7 +92,7 @@
         )
         val prevDrawMode = drawMode
         this.drawMode = drawMode
-        onDraw(Canvas(bitmap), screenBounds, calendar)
+        render(Canvas(bitmap), screenBounds, calendar)
         this.drawMode = prevDrawMode
         return bitmap
     }
@@ -116,7 +108,7 @@
      * @param calendar The current {@link Calendar}
      */
     @UiThread
-    protected abstract fun onDraw(
+    abstract fun render(
         canvas: Canvas,
         bounds: Rect,
         calendar: Calendar
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt
index c32bd26..872747c 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Complication.kt
@@ -28,65 +28,6 @@
 import androidx.wear.complications.SystemProviders
 import androidx.wear.complications.rendering.ComplicationDrawable
 
-/**
- * Complications use a ComplicationBoundsProvider to compute their bounds.
- */
-interface ComplicationBoundsProvider {
-    /**
-     * Computes the screen space bounds of the complication. This can be animated.
-     *
-     * @param complication The {@link Complication} to compute bounds for
-     * @param screen A {@link Rect} describing the bounds of the screen
-     */
-    fun computeBounds(complication: Complication, screen: Rect): Rect
-}
-
-/**
- * A {@link ComplicationBoundsProvider} for watch faces that wish to have a full screen user
- * selectable backdrop. This sort of complication isn't clickable and at most one may be present in
- * the list of complications.
- */
-class BackgroundComplicationBoundsProvider : ComplicationBoundsProvider {
-    /** {@inheritDoc} */
-    override fun computeBounds(
-        complication: Complication,
-        screen: Rect
-    ) = screen // A BackgroundComplication covers the whole screen.
-}
-
-/**
- * A {@link ComplicationBoundsProvider} which converts bounds provided in the unit square [0..1] to
- * device pixels. NB 0 and 1 are included in the unit square.
- */
-class UnitSquareBoundsProvider(
-    /**
-     * Fractional bounds for the complication (clamped to the unit square.) which get converted
-     * to screen space coordinates.
-     */
-    unitSquareBounds: RectF
-) : ComplicationBoundsProvider {
-
-    private companion object {
-        private val unitSquare = RectF(0f, 0f, 1f, 1f)
-    }
-
-    private val clampedUnitSquareBounds = RectF().apply {
-        setIntersect(
-            unitSquareBounds,
-            unitSquare
-        )
-    }
-
-    /** {@inheritDoc} */
-    override fun computeBounds(complication: Complication, screen: Rect) =
-        Rect(
-            (clampedUnitSquareBounds.left * screen.width()).toInt(),
-            (clampedUnitSquareBounds.top * screen.height()).toInt(),
-            (clampedUnitSquareBounds.right * screen.width()).toInt(),
-            (clampedUnitSquareBounds.bottom * screen.height()).toInt()
-        )
-}
-
 /** Common interface for rendering complications. */
 interface ComplicationRenderer {
     /**
@@ -254,31 +195,88 @@
  * Represents a individual complication on the screen. The number of complications is fixed
  * (see {@link ComplicationsHolder}) but complications can be enabled or disabled as needed.
  */
-class Complication @JvmOverloads constructor(
-    /** The watch face's ID for this complication. */
+class Complication internal constructor(
     internal val id: Int,
-
-    /**  An interface that can provide the bounds for this Complication. */
-    val boundsProvider: ComplicationBoundsProvider,
-
-    /**
-     * The renderer for this Complication. Renderers may not be sharable between complications.
-     */
-    internal var renderer: ComplicationRenderer,
-
-    /**
-     * The types of complication supported by this Complication. Passed into {@link
-     * ComplicationHelperActivity#createProviderChooserHelperIntent} during complication
-     * configuration.
-     */
+    @ComplicationBoundsType internal val boundsType: Int,
+    val unitSquareBounds: RectF,
+    renderer: ComplicationRenderer,
     internal val supportedTypes: IntArray,
-
-    /** Default complication provider. */
     internal val defaultProvider: DefaultComplicationProvider,
-
-    /** Default complication provider data type. */
-    internal val defaultProviderType: Int = WatchFace.DEFAULT_PROVIDER_TYPE_NONE
+    internal val defaultProviderType: Int
 ) {
+    private companion object {
+        internal val unitSquare = RectF(0f, 0f, 1f, 1f)
+    }
+
+    class Builder(
+        /** The watch face's ID for this complication. */
+        private val id: Int,
+
+        /**
+         * The renderer for this Complication. Renderers may not be sharable between complications.
+         */
+        private val renderer: ComplicationRenderer,
+
+        /**
+         * The types of complication supported by this Complication. Passed into {@link
+         * ComplicationHelperActivity#createProviderChooserHelperIntent} during complication
+         * configuration.
+         */
+        private val supportedTypes: IntArray,
+
+        /** Default complication provider. */
+        private val defaultProvider: DefaultComplicationProvider
+    ) {
+        @ComplicationBoundsType
+        private var boundsType: Int = ComplicationBoundsType.ROUND_RECT
+        private lateinit var unitSquareBounds: RectF
+
+        private var defaultProviderType: Int = WatchFace.DEFAULT_PROVIDER_TYPE_NONE
+
+        /** Sets the default complication provider data type. */
+        fun setDefaultProviderType(defaultProviderType: Int): Builder {
+            this.defaultProviderType = defaultProviderType
+            return this
+        }
+
+        /**
+         * Fractional bounds for the complication, clamped to the unit square [0..1], which get
+         * converted to screen space coordinates. NB 0 and 1 are included in the unit square.
+         */
+        fun setUnitSquareBounds(unitSquareBounds: RectF): Builder {
+            boundsType = ComplicationBoundsType.ROUND_RECT
+
+            this.unitSquareBounds = RectF().apply {
+                setIntersect(
+                    unitSquareBounds,
+                    unitSquare
+                )
+            }
+            return this
+        }
+
+        /**
+         * A background complication is for watch faces that wish to have a full screen user
+         * selectable backdrop. This sort of complication isn't clickable and at most one may be
+         * present in the list of complications.
+         */
+        fun setBackgroundComplication(): Builder {
+            boundsType = ComplicationBoundsType.BACKGROUND
+            this.unitSquareBounds = RectF(0f, 0f, 1f, 1f)
+            return this
+        }
+
+        fun build() = Complication(
+            id,
+            boundsType,
+            unitSquareBounds,
+            renderer,
+            supportedTypes,
+            defaultProvider,
+            defaultProviderType
+        )
+    }
+
     init {
         renderer.onAttach(this)
     }
@@ -310,7 +308,9 @@
     private var _enabled = true
 
     var enabled: Boolean
+        @UiThread
         get() = _enabled
+        @UiThread
         set(value) {
             _enabled = value
 
@@ -319,18 +319,19 @@
             complicationsHolder.scheduleUpdateActiveComplications()
         }
 
-    /**
-     * Sets the current {@link ComplicationRenderer}. This is useful if based on the style the
-     * watch face needs to use a different renderer.
-     */
-    @UiThread
-    fun setRenderer(renderer: ComplicationRenderer) {
-        renderer.onDetach()
-        renderer.setData(this.renderer.getData())
-        this.renderer = renderer
-        renderer.onAttach(this)
-        initRenderer()
-    }
+    private var _renderer = renderer
+
+    var renderer: ComplicationRenderer
+        @UiThread
+        get() = _renderer
+        @UiThread
+        set(value) {
+            renderer.onDetach()
+            value.setData(renderer.getData())
+            _renderer = value
+            value.onAttach(this)
+            initRenderer()
+        }
 
     /**
      * Watch faces should use this method to render a complication. Note the system may call this.
@@ -345,10 +346,7 @@
         calendar: Calendar,
         @DrawMode drawMode: Int
     ) {
-        val bounds = boundsProvider.computeBounds(
-            this,
-            Rect(0, 0, canvas.width, canvas.height)
-        )
+        val bounds = computeBounds(Rect(0, 0, canvas.width, canvas.height))
         renderer.onDraw(canvas, bounds, calendar, drawMode)
     }
 
@@ -386,4 +384,13 @@
             complicationsHolder.scheduleUpdateActiveComplications()
         }
     }
+
+    /** Computes the bounds of the complication by converting the unitSquareBounds to pixels. */
+    fun computeBounds(screen: Rect) =
+        Rect(
+            (unitSquareBounds.left * screen.width()).toInt(),
+            (unitSquareBounds.top * screen.height()).toInt(),
+            (unitSquareBounds.right * screen.width()).toInt(),
+            (unitSquareBounds.bottom * screen.height()).toInt()
+        )
 }
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationsHolder.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationsHolder.kt
index 7ec06d0..0302f6f 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationsHolder.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ComplicationsHolder.kt
@@ -141,17 +141,14 @@
                 // Generate a ContentDescriptionLabel and send complication bounds for
                 // non-background complications.
                 val data = complication.renderer.getData()
-                val complicationBounds = complication.boundsProvider.computeBounds(
-                    complication, renderer.screenBounds
-                )
-
-                if (complication.boundsProvider is BackgroundComplicationBoundsProvider) {
+                if (complication.boundsType == ComplicationBoundsType.BACKGROUND) {
                     watchFaceHostApi.setComplicationDetails(
                         id,
-                        complicationBounds,
+                        renderer.screenBounds,
                         ComplicationBoundsType.BACKGROUND
                     )
                 } else {
+                    val complicationBounds = complication.computeBounds(renderer.screenBounds)
                     if (data != null) {
                         labels.add(
                             ContentDescriptionLabel(
@@ -228,11 +225,8 @@
      */
     fun getComplicationAt(x: Int, y: Int): Complication? {
         return complications.entries.firstOrNull {
-            it.value.enabled && it.value.boundsProvider !is BackgroundComplicationBoundsProvider &&
-                    it.value.boundsProvider.computeBounds(
-                        it.value,
-                        renderer.screenBounds
-                    ).contains(x, y)
+            it.value.enabled && it.value.boundsType != ComplicationBoundsType.BACKGROUND &&
+                    it.value.computeBounds(renderer.screenBounds).contains(x, y)
         }?.value
     }
 
@@ -243,7 +237,7 @@
      */
     fun getBackgroundComplication(): Complication? {
         return complications.entries.firstOrNull {
-            it.value.boundsProvider is BackgroundComplicationBoundsProvider
+            it.value.boundsType == ComplicationBoundsType.BACKGROUND
         }?.value
     }
 
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Gles2Renderer.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Gles2Renderer.kt
index 466814c..1f47904 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Gles2Renderer.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Gles2Renderer.kt
@@ -234,11 +234,11 @@
     @UiThread
     open fun onGlSurfaceCreated(width: Int, height: Int) {}
 
-    internal override fun onDrawInternal(
+    internal override fun renderInternal(
         calendar: Calendar
     ) {
         makeContextCurrent()
-        onDraw(calendar)
+        render(calendar)
         if (!EGL14.eglSwapBuffers(eglDisplay, eglSurface)) {
             Log.w(TAG, "eglSwapBuffers failed")
         }
@@ -254,7 +254,7 @@
         val pixelBuf = ByteBuffer.allocateDirect(width * height * 4)
         makeContextCurrent()
         this.drawMode = drawMode
-        onDraw(calendar)
+        render(calendar)
         GLES20.glFinish()
         GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pixelBuf)
         val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
@@ -270,7 +270,5 @@
      * @param calendar The current {@link Calendar}
      */
     @UiThread
-    protected abstract fun onDraw(
-        calendar: Calendar
-    )
+    abstract fun render(calendar: Calendar)
 }
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
index 8652946..8266444 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/Renderer.kt
@@ -55,10 +55,9 @@
     var drawMode: Int
         get() = _drawMode ?: DrawMode.INTERACTIVE
         internal set(value) {
-            val newDrawMode = maybeOverrideDrawMode(value)
-            if (newDrawMode != _drawMode) {
-                _drawMode = newDrawMode
-                onDrawModeChanged(newDrawMode)
+            if (value != _drawMode) {
+                _drawMode = value
+                onDrawModeChanged(value)
             }
         }
 
@@ -77,9 +76,7 @@
      * @return A {@link Bitmap} containing a screenshot of the watch face
      */
     @UiThread
-    internal abstract fun onDrawInternal(
-        calendar: Calendar
-    )
+    internal abstract fun renderInternal(calendar: Calendar)
 
     /**
      * Renders the watch face into a Bitmap with the user style specified by the
@@ -140,7 +137,8 @@
     /**
      * The system periodically (at least once per minute) calls onTimeTick() to trigger a display
      * update. If the watch face needs to animate with an interactive frame rate, calls to
-     * invalidate must be scheduled. This method controls whether or not we should do that.
+     * invalidate must be scheduled. This method controls whether or not we should do that and if
+     * shouldAnimate returns true we inhibit entering {@link DrawMode#AMBIENT}.
      *
      * By default we remain at an interactive frame rate when the watch face is visible and we're
      * not in ambient mode. Watchfaces with animated transitions for entering ambient mode may
@@ -150,15 +148,4 @@
      */
     @UiThread
     open fun shouldAnimate() = watchState.isVisible && !watchState.isAmbient
-
-    /**
-     * The {@link DrawMode} is recomputed before every onDraw call. This method allows the subclass
-     * to override the {@link DrawMode}. e.g. to support enter/exit ambient animations which may
-     * wish to defer rendering changes.
-     *
-     * @param proposedDrawMode The proposed {@link DrawMode} to use for rendering based
-     * @return The {@link DrawMode} to actually use for rendering
-     */
-    @UiThread
-    open fun maybeOverrideDrawMode(@DrawMode proposedDrawMode: Int) = proposedDrawMode
 }
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index 22592fa..abff28e 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -676,7 +676,9 @@
         } else {
             DrawMode.INTERACTIVE
         }
-        if (watchState.isAmbient) {
+        // Watch faces may wish to run an animation while entering ambient mode and we let them
+        // defer entering ambient mode.
+        if (watchState.isAmbient && !renderer.shouldAnimate()) {
             newDrawMode = DrawMode.AMBIENT
         } else if (muteMode) {
             newDrawMode = DrawMode.MUTE
@@ -689,7 +691,7 @@
     internal fun onDraw() {
         setCalendarTime(systemTimeProvider.getSystemTimeMillis())
         maybeUpdateDrawMode()
-        renderer.onDrawInternal(calendar)
+        renderer.renderInternal(calendar)
 
         val currentTimeMillis = systemTimeProvider.getSystemTimeMillis()
         setCalendarTime(currentTimeMillis)
@@ -703,7 +705,7 @@
     internal fun onSurfaceRedrawNeeded() {
         setCalendarTime(systemTimeProvider.getSystemTimeMillis())
         maybeUpdateDrawMode()
-        renderer.onDrawInternal(calendar)
+        renderer.renderInternal(calendar)
     }
 
     /** @hide */
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index d7c24ea..5fef9c6 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -523,10 +523,7 @@
                     }
                     val complication = watchFace.complicationsHolder[complicationId]
                     if (complication != null) {
-                        val bounds = complication.boundsProvider.computeBounds(
-                            complication,
-                            watchFace.renderer.screenBounds
-                        )
+                        val bounds = complication.computeBounds(watchFace.renderer.screenBounds)
                         val complicationBitmap =
                             Bitmap.createBitmap(
                                 bounds.width(), bounds.height(),
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt
index 9f4d3b3..e80d7a9 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/WatchState.kt
@@ -34,6 +34,7 @@
      * Whether or not the watch is in airplane mode. Only valid if
      * {@link android.support.wearable.watchface.WatchFaceStyle#hideNotificationIndicator} is true.
      */
+    @get:JvmName("inAirplaneMode")
     var inAirplaneMode = false
         private set
 
@@ -92,10 +93,12 @@
         private set
 
     /** Whether or not the watch has low bit ambient support. */
+    @get:JvmName("hasLowBitAmbient")
     var hasLowBitAmbient = false
         private set
 
     /** Whether or not the watch has burn in protection support. */
+    @get:JvmName("hasBurnInProtection")
     var hasBurnInProtection = false
         private set
 
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ConfigFragment.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ConfigFragment.kt
index 3008b28..de2db2c 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ConfigFragment.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/ConfigFragment.kt
@@ -216,24 +216,30 @@
     var configOption: ConfigOption? = null
 }
 
-/**
- * Wraps a given [Drawable] with a standard background to match the normal preference icon
- * styling. The wrapping is idempotent, calling it multiple times will only wrap the icon once.
- *
- * @param context the current [Context], used for resolving resources.
- * @param icon the icon to wrap.
- * @return the wrapped icon.
- */
-internal fun wrapIcon(
-    context: Context,
-    icon: Drawable
-): Drawable {
-    if (icon is LayerDrawable && icon.findDrawableByLayerId(R.id.nested_icon) != null) {
-        return icon // icon was already wrapped, return the icon without modifying it
+internal class Helper {
+    companion object {
+        /**
+         * Wraps a given [Drawable] with a standard background to match the normal preference icon
+         * styling. The wrapping is idempotent, calling it multiple times will only wrap the icon
+         * once.
+         *
+         * @param context the current [Context], used for resolving resources.
+         * @param icon the icon to wrap.
+         * @return the wrapped icon.
+         */
+        fun wrapIcon(
+            context: Context,
+            icon: Drawable
+        ): Drawable {
+            if (icon is LayerDrawable && icon.findDrawableByLayerId(R.id.nested_icon) != null) {
+                return icon // icon was already wrapped, return the icon without modifying it
+            }
+            val wrappedDrawable =
+                (context.getDrawable(R.drawable.preference_wrapped_icon) as LayerDrawable)
+            wrappedDrawable.setDrawableByLayerId(R.id.nested_icon, icon)
+            return wrappedDrawable
+        }
     }
-    val wrappedDrawable = (context.getDrawable(R.drawable.preference_wrapped_icon) as LayerDrawable)
-    wrappedDrawable.setDrawableByLayerId(R.id.nested_icon, icon)
-    return wrappedDrawable
 }
 
 /** Adapter for top level config options. */
@@ -272,7 +278,7 @@
             context,
             { drawable ->
                 textView.setCompoundDrawablesRelativeWithIntrinsicBounds(
-                    wrapIcon(context, drawable),
+                    Helper.wrapIcon(context, drawable),
                     /* top = */ null,
                     /* end = */ null,
                     /* bottom = */ null
diff --git a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/StyleConfigFragment.kt b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/StyleConfigFragment.kt
index 5b4b921..b545795 100644
--- a/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/StyleConfigFragment.kt
+++ b/wear/wear-watchface/src/main/java/androidx/wear/watchface/ui/StyleConfigFragment.kt
@@ -249,7 +249,7 @@
             context,
             { drawable ->
                 textView.setCompoundDrawablesRelativeWithIntrinsicBounds(
-                    wrapIcon(context, drawable),
+                    Helper.wrapIcon(context, drawable),
                     /* top = */ null,
                     /* end = */ null,
                     /* bottom = */ null
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
index bba9724..72a714b 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
@@ -203,7 +203,7 @@
     }
 }
 
-class TestRenderer(
+open class TestRenderer(
     surfaceHolder: SurfaceHolder,
     userStyleManager: UserStyleManager,
     watchState: WatchState
@@ -212,7 +212,7 @@
     var lastOnDrawCalendar: Calendar? = null
     var lastDrawMode = DrawMode.INTERACTIVE
 
-    override fun onDraw(
+    override fun render(
         canvas: Canvas,
         bounds: Rect,
         calendar: Calendar
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index d46bf7d..33a074a 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -130,9 +130,8 @@
         ListUserStyleCategory.ListOption("bad_option", "Bad", icon = null)
 
     private val leftComplication =
-        Complication(
+        Complication.Builder(
             LEFT_COMPLICATION_ID,
-            UnitSquareBoundsProvider(RectF(0.2f, 0.4f, 0.4f, 0.6f)),
             ComplicationDrawableRenderer(complicationDrawableLeft, systemState).apply {
                 setData(createComplicationData())
             },
@@ -143,14 +142,14 @@
                 ComplicationData.TYPE_ICON,
                 ComplicationData.TYPE_SMALL_IMAGE
             ),
-            Complication.DefaultComplicationProvider(SystemProviders.SUNRISE_SUNSET),
-            ComplicationData.TYPE_SHORT_TEXT
-        )
+            Complication.DefaultComplicationProvider(SystemProviders.SUNRISE_SUNSET)
+        ).setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+            .setUnitSquareBounds(RectF(0.2f, 0.4f, 0.4f, 0.6f))
+            .build()
 
     private val rightComplication =
-        Complication(
+        Complication.Builder(
             RIGHT_COMPLICATION_ID,
-            UnitSquareBoundsProvider(RectF(0.6f, 0.4f, 0.8f, 0.6f)),
             ComplicationDrawableRenderer(complicationDrawableRight, systemState).apply {
                 setData(createComplicationData())
             },
@@ -161,23 +160,24 @@
                 ComplicationData.TYPE_ICON,
                 ComplicationData.TYPE_SMALL_IMAGE
             ),
-            Complication.DefaultComplicationProvider(SystemProviders.DAY_OF_WEEK),
-            ComplicationData.TYPE_SHORT_TEXT
-        )
+            Complication.DefaultComplicationProvider(SystemProviders.DAY_OF_WEEK)
+        ).setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+            .setUnitSquareBounds(RectF(0.6f, 0.4f, 0.8f, 0.6f))
+            .build()
 
     private val backgroundComplication =
-        Complication(
+        Complication.Builder(
             BACKGROUND_COMPLICATION_ID,
-            BackgroundComplicationBoundsProvider(),
             ComplicationDrawableRenderer(complicationDrawableRight, systemState).apply {
                 setData(createComplicationData())
             },
             intArrayOf(
                 ComplicationData.TYPE_LARGE_IMAGE
             ),
-            Complication.DefaultComplicationProvider(),
-            ComplicationData.TYPE_LARGE_IMAGE
-        )
+            Complication.DefaultComplicationProvider()
+        ).setDefaultProviderType(ComplicationData.TYPE_LARGE_IMAGE)
+            .setBackgroundComplication()
+            .build()
 
     private lateinit var renderer: TestRenderer
     private lateinit var complicationsHolder: ComplicationsHolder
@@ -1049,17 +1049,17 @@
     fun defaultProvidersWithFallbacks_newApi() {
         val provider1 = ComponentName("com.app1", "com.app1.App1")
         val provider2 = ComponentName("com.app2", "com.app2.App2")
-        val complication = Complication(
+        val complication = Complication.Builder(
             LEFT_COMPLICATION_ID,
-            UnitSquareBoundsProvider(RectF(0.2f, 0.4f, 0.4f, 0.6f)),
             ComplicationDrawableRenderer(complicationDrawableLeft, systemState),
             intArrayOf(),
             Complication.DefaultComplicationProvider(
                 listOf(provider1, provider2),
                 SystemProviders.SUNRISE_SUNSET
-            ),
-            ComplicationData.TYPE_SHORT_TEXT
-        )
+            )
+        ).setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+            .setUnitSquareBounds(RectF(0.2f, 0.4f, 0.4f, 0.6f))
+            .build()
         initEngine(WatchFaceType.ANALOG, listOf(complication), emptyList())
 
         verify(iWatchFaceService).setDefaultComplicationProviderWithFallbacks(
@@ -1074,17 +1074,17 @@
     fun defaultProvidersWithFallbacks_oldApi() {
         val provider1 = ComponentName("com.app1", "com.app1.App1")
         val provider2 = ComponentName("com.app2", "com.app2.App2")
-        val complication = Complication(
+        val complication = Complication.Builder(
             LEFT_COMPLICATION_ID,
-            UnitSquareBoundsProvider(RectF(0.2f, 0.4f, 0.4f, 0.6f)),
             ComplicationDrawableRenderer(complicationDrawableLeft, systemState),
             intArrayOf(),
             Complication.DefaultComplicationProvider(
                 listOf(provider1, provider2),
                 SystemProviders.SUNRISE_SUNSET
-            ),
-            ComplicationData.TYPE_SHORT_TEXT
-        )
+            )
+        ).setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+            .setUnitSquareBounds(RectF(0.2f, 0.4f, 0.4f, 0.6f))
+            .build()
         initEngine(WatchFaceType.ANALOG, listOf(complication), emptyList(), apiVersion = 0)
 
         verify(iWatchFaceService).setDefaultComplicationProvider(
@@ -1109,4 +1109,41 @@
         initEngine(WatchFaceType.DIGITAL, emptyList(), emptyList(), apiVersion = 4)
         verify(iWatchFaceService).registerIWatchFaceCommand(any())
     }
+
+    @Test
+    fun shouldAnimateOverrideControlsEnteringAmbientMode() {
+        var styleManager = UserStyleManager(emptyList())
+        var testRenderer = object : TestRenderer(surfaceHolder, styleManager, systemState) {
+            var animate = true
+            override fun shouldAnimate() = animate
+        }
+        val service = TestWatchFaceService(
+            WatchFaceType.ANALOG,
+            ComplicationsHolder(emptyList()),
+            testRenderer,
+            UserStyleManager(emptyList()),
+            systemState,
+            handler,
+            INTERACTIVE_UPDATE_RATE_MS
+        )
+
+        engineWrapper = service.onCreateEngine() as WatchFaceService.EngineWrapper
+        engineWrapper.onCreate(surfaceHolder)
+        `when`(surfaceHolder.surfaceFrame).thenReturn(ONE_HUNDRED_BY_ONE_HUNDRED_RECT)
+
+        // Trigger watch face creation.
+        engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
+        sendBinder(engineWrapper, apiVersion = 2)
+        watchFace = service.watchFace
+
+        // Enter ambient mode.
+        systemState.onAmbientModeChanged(true)
+        watchFace.maybeUpdateDrawMode()
+        assertThat(testRenderer.drawMode).isEqualTo(DrawMode.INTERACTIVE)
+
+        // Simulate enter ambient animation finishing.
+        testRenderer.animate = false
+        watchFace.maybeUpdateDrawMode()
+        assertThat(testRenderer.drawMode).isEqualTo(DrawMode.AMBIENT)
+    }
 }
diff --git a/wear/wear-watchface/src/test/java/androidx/wear/watchface/ui/WatchFaceConfigUiTest.kt b/wear/wear-watchface/src/test/java/androidx/wear/watchface/ui/WatchFaceConfigUiTest.kt
index 62e8758..9d78007 100644
--- a/wear/wear-watchface/src/test/java/androidx/wear/watchface/ui/WatchFaceConfigUiTest.kt
+++ b/wear/wear-watchface/src/test/java/androidx/wear/watchface/ui/WatchFaceConfigUiTest.kt
@@ -29,11 +29,9 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.wear.complications.SystemProviders
 import androidx.wear.complications.rendering.ComplicationDrawable
-import androidx.wear.watchface.BackgroundComplicationBoundsProvider
 import androidx.wear.watchface.Complication
 import androidx.wear.watchface.ComplicationDrawableRenderer
 import androidx.wear.watchface.ComplicationsHolder
-import androidx.wear.watchface.UnitSquareBoundsProvider
 import androidx.wear.watchface.Renderer
 import androidx.wear.watchface.WatchState
 import androidx.wear.watchface.WatchFaceTestRunner
@@ -114,9 +112,8 @@
     )
 
     private val leftComplication =
-        Complication(
+        Complication.Builder(
             LEFT_COMPLICATION_ID,
-            UnitSquareBoundsProvider(RectF(0.2f, 0.4f, 0.4f, 0.6f)),
             ComplicationDrawableRenderer(complicationDrawableLeft, systemState).apply {
                 setData(createComplicationData() )
             },
@@ -127,14 +124,14 @@
                 ComplicationData.TYPE_ICON,
                 ComplicationData.TYPE_SMALL_IMAGE
             ),
-            Complication.DefaultComplicationProvider(SystemProviders.SUNRISE_SUNSET),
-            ComplicationData.TYPE_SHORT_TEXT
-        )
+            Complication.DefaultComplicationProvider(SystemProviders.SUNRISE_SUNSET)
+        ).setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+            .setUnitSquareBounds(RectF(0.2f, 0.4f, 0.4f, 0.6f))
+            .build()
 
     private val rightComplication =
-        Complication(
+        Complication.Builder(
             RIGHT_COMPLICATION_ID,
-            UnitSquareBoundsProvider(RectF(0.6f, 0.4f, 0.8f, 0.6f)),
             ComplicationDrawableRenderer(complicationDrawableRight, systemState).apply {
                 setData(createComplicationData() )
             },
@@ -145,23 +142,24 @@
                 ComplicationData.TYPE_ICON,
                 ComplicationData.TYPE_SMALL_IMAGE
             ),
-            Complication.DefaultComplicationProvider(SystemProviders.DAY_OF_WEEK),
-            ComplicationData.TYPE_SHORT_TEXT
-        )
+            Complication.DefaultComplicationProvider(SystemProviders.DAY_OF_WEEK)
+        ).setDefaultProviderType(ComplicationData.TYPE_SHORT_TEXT)
+            .setUnitSquareBounds(RectF(0.6f, 0.4f, 0.8f, 0.6f))
+            .build()
 
     private val backgroundComplication =
-        Complication(
+        Complication.Builder(
             BACKGROUND_COMPLICATION_ID,
-            BackgroundComplicationBoundsProvider(),
             ComplicationDrawableRenderer(complicationDrawableRight, systemState).apply {
                 setData(createComplicationData() )
             },
             intArrayOf(
                 ComplicationData.TYPE_LARGE_IMAGE
             ),
-            Complication.DefaultComplicationProvider(),
-            ComplicationData.TYPE_LARGE_IMAGE
-        )
+            Complication.DefaultComplicationProvider()
+        ).setDefaultProviderType(ComplicationData.TYPE_LARGE_IMAGE)
+            .setBackgroundComplication()
+            .build()
 
     private val calendar = Calendar.getInstance().apply {
         timeInMillis = 1000L
@@ -184,7 +182,7 @@
         val complicationSet = ComplicationsHolder(
             complications,
             object : Renderer(surfaceHolder, userStyleManager, WatchState()) {
-                override fun onDrawInternal(calendar: Calendar) {}
+                override fun renderInternal(calendar: Calendar) {}
 
                 override fun takeScreenshot(calendar: Calendar, drawMode: Int): Bitmap {
                     throw RuntimeException("Not Implemented!")