Merge "Add new tests for generic and inherited types." into androidx-main
diff --git a/OWNERS b/OWNERS
index c325dad..6e081c7 100644
--- a/OWNERS
+++ b/OWNERS
@@ -11,6 +11,7 @@
 jsproch@google.com
 lpf@google.com
 mount@google.com
+natnaelbelay@google.com
 nickanthony@google.com
 owengray@google.com
 romainguy@android.com
diff --git a/appcompat/appcompat-lint/build.gradle b/appcompat/appcompat-lint/build.gradle
index ecd2347..847916f 100644
--- a/appcompat/appcompat-lint/build.gradle
+++ b/appcompat/appcompat-lint/build.gradle
@@ -35,7 +35,6 @@
 androidx {
     name = "AppCompat Lint Checks"
     type = LibraryType.LINT
-    mavenVersion = LibraryVersions.APPCOMPAT
     mavenGroup = LibraryGroups.APPCOMPAT
     inceptionYear = "2019"
     description = "AppCompat Lint Checks"
diff --git a/appcompat/appcompat-resources/build.gradle b/appcompat/appcompat-resources/build.gradle
index db493d4..5ad3701 100644
--- a/appcompat/appcompat-resources/build.gradle
+++ b/appcompat/appcompat-resources/build.gradle
@@ -23,6 +23,11 @@
 }
 
 dependencies {
+    // Atomic group
+    constraints {
+        implementation(project(":appcompat:appcompat"))
+    }
+
     api("androidx.annotation:annotation:1.2.0")
     api("androidx.core:core:1.6.0")
     implementation("androidx.collection:collection:1.0.0")
@@ -61,7 +66,6 @@
 androidx {
     name = "Android Resources Library"
     publish = Publish.SNAPSHOT_AND_RELEASE
-    mavenVersion = LibraryVersions.APPCOMPAT
     mavenGroup = LibraryGroups.APPCOMPAT
     inceptionYear = "2019"
     description = "The Resources Library is a static library that you can add to your Android application in order to use resource APIs that backport the latest APIs to older versions of the platform. Compatible on devices running API 14 or later."
diff --git a/appcompat/appcompat/build.gradle b/appcompat/appcompat/build.gradle
index 57a4658..5ad588a 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -8,6 +8,11 @@
 }
 
 dependencies {
+    // Atomic group
+    constraints {
+        implementation(project(":appcompat:appcompat-resources"))
+    }
+
     api("androidx.annotation:annotation:1.3.0")
     api(projectOrArtifact(":core:core"))
 
@@ -93,7 +98,6 @@
 androidx {
     name = "Android AppCompat Library"
     publish = Publish.SNAPSHOT_AND_RELEASE
-    mavenVersion = LibraryVersions.APPCOMPAT
     mavenGroup = LibraryGroups.APPCOMPAT
     inceptionYear = "2011"
     description = "The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren\'t a part of the framework APIs. Compatible on devices running API 14 or later."
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseViewTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseViewTest.java
index 6321a86..83fa83c4 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseViewTest.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatBaseViewTest.java
@@ -25,6 +25,8 @@
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.testutils.LifecycleOwnerUtils.waitUntilState;
+import static androidx.testutils.PollingCheck.waitFor;
 
 import static org.junit.Assert.assertNull;
 
@@ -44,17 +46,18 @@
 import androidx.appcompat.testutils.TestUtils;
 import androidx.core.content.res.ResourcesCompat;
 import androidx.core.graphics.ColorUtils;
-import androidx.test.espresso.UiController;
+import androidx.lifecycle.Lifecycle;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
-import androidx.test.rule.ActivityTestRule;
-import androidx.testutils.PollingCheck;
 
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.atomic.AtomicReference;
+
 /**
  * Base class for testing custom view extensions in appcompat-v7 that implement the
  * <code>TintableBackgroundView</code> interface. Extensions of this class run all tests
@@ -65,26 +68,35 @@
 @MediumTest
 public abstract class AppCompatBaseViewTest<A extends BaseTestActivity, T extends View> {
     @Rule
-    public final ActivityTestRule<A> mActivityTestRule;
+    public final ActivityScenarioRule<A> mActivityTestRule;
 
     protected ViewGroup mContainer;
 
     protected A mActivity;
-    protected UiController mUiController;
     protected Resources mResources;
 
     public AppCompatBaseViewTest(Class<A> clazz) {
-        mActivityTestRule = new ActivityTestRule<>(clazz);
+        mActivityTestRule = new ActivityScenarioRule<>(clazz);
     }
 
     @Before
     public void setUp() {
-        mActivity = mActivityTestRule.getActivity();
+        AtomicReference<A> outerActivity = new AtomicReference<>();
+        mActivityTestRule.getScenario().onActivity(outerActivity::set);
+
+        mActivity = outerActivity.get();
         mContainer = mActivity.findViewById(R.id.container);
         mResources = mActivity.getResources();
 
+        // Wait until the Activity is resumed to prevent flakiness.
+        try {
+            waitUntilState(mActivity, Lifecycle.State.RESUMED);
+        } catch (Throwable e) {
+            throw new RuntimeException(e);
+        }
+
         // Wait until the Activity is interactive to prevent flakiness.
-        PollingCheck.waitFor(() -> mActivity.hasWindowFocus());
+        waitFor(() -> mActivity.hasWindowFocus());
     }
 
     /**
@@ -632,8 +644,6 @@
     }
 
     protected void testUntintedBackgroundTintingViewCompatAcrossStateChange(@IdRes int viewId) {
-        final T view = mContainer.findViewById(viewId);
-
         final @ColorInt int oceanDefault = ResourcesCompat.getColor(
                 mResources, R.color.ocean_default, null);
         final @ColorInt int oceanDisabled = ResourcesCompat.getColor(
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextViewTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextViewTest.java
index db2396e..86cfcb1 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextViewTest.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatMultiAutoCompleteTextViewTest.java
@@ -36,7 +36,6 @@
 import androidx.core.graphics.ColorUtils;
 import androidx.core.widget.TextViewCompat;
 import androidx.test.annotation.UiThreadTest;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SdkSuppress;
 
@@ -137,7 +136,6 @@
                 TextViewCompat.getCompoundDrawableTintList(textView));
     }
 
-    @FlakyTest(bugId = 236861901)
     @Test
     public void testCompoundDrawablesTintList() {
         // Given an ACTV with a white drawableLeftCompat and a ColorStateList drawableTint set
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatSpinnerRtlTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatSpinnerRtlTest.java
index ba59ede..3ff4329 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatSpinnerRtlTest.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatSpinnerRtlTest.java
@@ -18,7 +18,6 @@
 import android.app.Instrumentation;
 import android.os.Build;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -50,7 +49,6 @@
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
     }
 
-    @FlakyTest
     @Test
     public void testHorizontalOffsetRtl() {
         AppCompatSpinnerTest.checkOffsetIsCorrect(mInstrumentation, mContainer, 200, false, true);
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatSpinnerTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatSpinnerTest.java
index 7fc0315..822d04e 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatSpinnerTest.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatSpinnerTest.java
@@ -48,7 +48,6 @@
 import androidx.test.espresso.action.GeneralSwipeAction;
 import androidx.test.espresso.action.Press;
 import androidx.test.espresso.action.Swipe;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SdkSuppress;
@@ -150,12 +149,11 @@
 
         // Set a different popup background
         spinner.setPopupBackgroundDrawable(ContextCompat.getDrawable(
-                mActivityTestRule.getActivity(), R.drawable.test_background_green));
+                mActivity, R.drawable.test_background_green));
         verifySpinnerPopupTheming(R.id.view_unthemed_popup, R.color.test_green);
     }
 
     @Test
-    @FlakyTest
     public void testThemedPopupRuntimeTheming() {
         final AppCompatSpinner spinner =
                 mContainer.findViewById(R.id.view_ocean_themed_popup);
@@ -167,7 +165,7 @@
 
         // Set a different popup background
         spinner.setPopupBackgroundDrawable(ContextCompat.getDrawable(
-                mActivityTestRule.getActivity(), R.drawable.test_background_blue));
+                mActivity, R.drawable.test_background_blue));
         verifySpinnerPopupTheming(R.id.view_ocean_themed_popup, R.color.test_blue);
     }
 
@@ -188,7 +186,6 @@
     }
 
     @Test
-    @FlakyTest
     public void testSlowScroll() {
         final AppCompatSpinner spinner = mContainer
                 .findViewById(R.id.spinner_dropdown_popup_with_scroll);
@@ -210,7 +207,6 @@
     }
 
     @Test
-    @FlakyTest
     public void testHorizontalOffset() {
         checkOffsetIsCorrect(mInstrumentation, mContainer, 500, false, false);
     }
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewTest.java b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewTest.java
index 8276a2b..d392322 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewTest.java
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewTest.java
@@ -53,6 +53,7 @@
 import androidx.annotation.ColorInt;
 import androidx.annotation.ColorRes;
 import androidx.annotation.GuardedBy;
+import androidx.annotation.RequiresApi;
 import androidx.appcompat.content.res.AppCompatResources;
 import androidx.appcompat.test.R;
 import androidx.appcompat.testutils.TestUtils;
@@ -63,12 +64,12 @@
 import androidx.core.view.ViewCompat;
 import androidx.core.widget.TextViewCompat;
 import androidx.test.annotation.UiThreadTest;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SdkSuppress;
 import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
 
+import org.junit.Ignore;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -116,13 +117,10 @@
         // Emulate delay in kicking off the call to ViewCompat.setBackgroundTintList
         Thread.sleep(200);
         final CountDownLatch latch = new CountDownLatch(1);
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                TextView view = mActivity.findViewById(R.id.view_untinted_deferred);
-                ViewCompat.setBackgroundTintList(view, oceanColor);
-                latch.countDown();
-            }
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            TextView view = activity.findViewById(R.id.view_untinted_deferred);
+            ViewCompat.setBackgroundTintList(view, oceanColor);
+            latch.countDown();
         });
 
         assertTrue(latch.await(2, TimeUnit.SECONDS));
@@ -579,177 +577,142 @@
     }
 
     @Test
-    @FlakyTest
-    public void testSetTextFuture() throws Throwable {
+    public void testSetTextFuture() {
         final ManualExecutor executor = new ManualExecutor();
 
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                AppCompatTextView tv = mActivity.findViewById(R.id.textview_set_text_async);
-                tv.setText(""); // Make the measured width to be zero.
-                tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
-                        SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), executor));
-            }
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            AppCompatTextView tv = activity.findViewById(R.id.textview_set_text_async);
+            tv.setText(""); // Make the measured width to be zero.
+            tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
+                    SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), executor));
         });
 
         executor.doExecution(0);
 
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                AppCompatTextView tv = mActivity.findViewById(R.id.textview_set_text_async);
-                tv.measure(UNLIMITED_MEASURE_SPEC, UNLIMITED_MEASURE_SPEC);
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            AppCompatTextView tv = activity.findViewById(R.id.textview_set_text_async);
+            tv.measure(UNLIMITED_MEASURE_SPEC, UNLIMITED_MEASURE_SPEC);
 
-                // 0 is a initial value of the text view width of this test. setTextFuture should
-                // block and set text inside measure method. So, the result of measurment should not
-                // be zero
-                assertNotEquals(0.0f, tv.getMeasuredWidth());
-                // setText may wrap the given text with SpannedString. Check the contents by casting
-                // to String.
-                assertEquals(SAMPLE_TEXT_1, tv.getText().toString());
-            }
+            // 0 is a initial value of the text view width of this test. setTextFuture should
+            // block and set text inside measure method. So, the result of measurment should not
+            // be zero
+            assertNotEquals(0.0f, tv.getMeasuredWidth());
+            // setText may wrap the given text with SpannedString. Check the contents by casting
+            // to String.
+            assertEquals(SAMPLE_TEXT_1, tv.getText().toString());
         });
     }
 
+    @Ignore // Test blocks forever.
     @Test
-    @FlakyTest
-    public void testSetTextAsync_getTextBlockingTest() throws Throwable {
+    public void testSetTextAsync_getTextBlockingTest() {
         final ManualExecutor executor = new ManualExecutor();
-        mActivity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                final AppCompatTextView tv = mActivity.findViewById(R.id.textview_set_text_async);
-                tv.setText(""); // Make the measured width to be zero.
-                tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
-                        SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), executor));
-                tv.measure(UNLIMITED_MEASURE_SPEC, UNLIMITED_MEASURE_SPEC);
-                assertNotEquals(0.0f, tv.getMeasuredWidth());
-                assertEquals(SAMPLE_TEXT_1, tv.getText().toString());
-            }
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            final AppCompatTextView tv = activity.findViewById(R.id.textview_set_text_async);
+            tv.setText(""); // Make the measured width to be zero.
+            tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
+                    SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), executor));
+            tv.measure(UNLIMITED_MEASURE_SPEC, UNLIMITED_MEASURE_SPEC);
+            assertNotEquals(0.0f, tv.getMeasuredWidth());
+            assertEquals(SAMPLE_TEXT_1, tv.getText().toString());
         });
         executor.doExecution(0);
     }
 
     @Test
-    @FlakyTest
-    public void testSetTextAsync_executionOrder() throws Throwable {
+    public void testSetTextAsync_executionOrder() {
         final ManualExecutor executor = new ManualExecutor();
-        mActivity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                final AppCompatTextView tv = mActivity.findViewById(R.id.textview_set_text_async);
-                tv.setText(""); // Make the measured width to be zero.
-                tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
-                        SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), executor));
-                tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
-                        SAMPLE_TEXT_2, tv.getTextMetricsParamsCompat(), executor));
-            }
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            final AppCompatTextView tv = activity.findViewById(R.id.textview_set_text_async);
+            tv.setText(""); // Make the measured width to be zero.
+            tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
+                    SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), executor));
+            tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
+                    SAMPLE_TEXT_2, tv.getTextMetricsParamsCompat(), executor));
         });
         executor.doExecution(1);  // Do execution of 2nd runnable.
-        mActivity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                final AppCompatTextView tv = mActivity.findViewById(R.id.textview_set_text_async);
-                tv.measure(UNLIMITED_MEASURE_SPEC, UNLIMITED_MEASURE_SPEC);
-                assertNotEquals(0.0f, tv.getMeasuredWidth());
-                // setText may wrap the given text with SpannedString. Check the contents by casting
-                // to String.
-                assertEquals(SAMPLE_TEXT_2, tv.getText().toString());
-            }
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            final AppCompatTextView tv = activity.findViewById(R.id.textview_set_text_async);
+            tv.measure(UNLIMITED_MEASURE_SPEC, UNLIMITED_MEASURE_SPEC);
+            assertNotEquals(0.0f, tv.getMeasuredWidth());
+            // setText may wrap the given text with SpannedString. Check the contents by casting
+            // to String.
+            assertEquals(SAMPLE_TEXT_2, tv.getText().toString());
         });
         executor.doExecution(0);  // Do execution of 1st runnable.
         // Even the previous setTextAsync finishes after the next setTextAsync, the last one should
         // be displayed.
-        mActivity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                final AppCompatTextView tv = mActivity.findViewById(R.id.textview_set_text_async);
-                // setText may wrap the given text with SpannedString. Check the contents by casting
-                // to String.
-                assertEquals(SAMPLE_TEXT_2, tv.getText().toString());
-            }
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            final AppCompatTextView tv = activity.findViewById(R.id.textview_set_text_async);
+            // setText may wrap the given text with SpannedString. Check the contents by casting
+            // to String.
+            assertEquals(SAMPLE_TEXT_2, tv.getText().toString());
         });
     }
 
     @Test
-    public void testSetTextAsync_directionDifference() throws Throwable {
-        mActivity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mActivity.setContentView(R.layout.appcompat_textview_rtl);
-                final ViewGroup container = mActivity.findViewById(R.id.container);
-                final AppCompatTextView tv = mActivity.findViewById(R.id.text_view_rtl);
-                tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
-                        SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), null));
-                container.measure(UNLIMITED_MEASURE_SPEC, UNLIMITED_MEASURE_SPEC);
-            }
+    public void testSetTextAsync_directionDifference() {
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            activity.setContentView(R.layout.appcompat_textview_rtl);
+            final ViewGroup container = activity.findViewById(R.id.container);
+            final AppCompatTextView tv = activity.findViewById(R.id.text_view_rtl);
+            tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
+                    SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), null));
+            container.measure(UNLIMITED_MEASURE_SPEC, UNLIMITED_MEASURE_SPEC);
         });
     }
 
     @Test
-    public void testSetTextAsync_createAndAttach() throws Throwable {
-        mActivity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mActivity.setContentView(R.layout.appcompat_textview_rtl);
-                final ViewGroup container = mActivity.findViewById(R.id.container);
+    public void testSetTextAsync_createAndAttach() {
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            activity.setContentView(R.layout.appcompat_textview_rtl);
+            final ViewGroup container = activity.findViewById(R.id.container);
 
-                final AppCompatTextView tv = new AppCompatTextView(mActivity);
-                tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
-                        SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), null));
-                container.addView(tv);
-                container.measure(UNLIMITED_MEASURE_SPEC, UNLIMITED_MEASURE_SPEC);
-            }
+            final AppCompatTextView tv = new AppCompatTextView(activity);
+            tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
+                    SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), null));
+            container.addView(tv);
+            container.measure(UNLIMITED_MEASURE_SPEC, UNLIMITED_MEASURE_SPEC);
         });
     }
 
     @Test
-    public void testSetTextAsync_executionOrder_withNull() throws Throwable {
+    public void testSetTextAsync_executionOrder_withNull() {
         final ManualExecutor executor = new ManualExecutor();
-        mActivity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                final AppCompatTextView tv = mActivity.findViewById(R.id.textview_set_text_async);
-                tv.setText(""); // Make the measured width to be zero.
-                tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
-                        SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), executor));
-                tv.setTextFuture(null);
-            }
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            final AppCompatTextView tv = activity.findViewById(R.id.textview_set_text_async);
+            tv.setText(""); // Make the measured width to be zero.
+            tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
+                    SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), executor));
+            tv.setTextFuture(null);
         });
         executor.doExecution(0);  // Do execution of 1st runnable.
-        mActivity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                final AppCompatTextView tv = mActivity.findViewById(R.id.textview_set_text_async);
-                tv.measure(UNLIMITED_MEASURE_SPEC, UNLIMITED_MEASURE_SPEC);
-                // The setTextFuture was reset by passing null.
-                assertEquals(0.0f, tv.getMeasuredWidth(), 0.0f);
-            }
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            final AppCompatTextView tv = activity.findViewById(R.id.textview_set_text_async);
+            tv.measure(UNLIMITED_MEASURE_SPEC, UNLIMITED_MEASURE_SPEC);
+            // The setTextFuture was reset by passing null.
+            assertEquals(0.0f, tv.getMeasuredWidth(), 0.0f);
         });
     }
 
     @Test
-    public void testSetTextAsync_throwExceptionAfterSetTextFuture() throws Throwable {
+    public void testSetTextAsync_throwExceptionAfterSetTextFuture() {
         final ManualExecutor executor = new ManualExecutor();
-        mActivity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                final AppCompatTextView tv = mActivity.findViewById(R.id.textview_set_text_async);
-                tv.setText(""); // Make the measured width to be zero.
-                tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
-                        SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), executor));
-                tv.setTextSize(tv.getTextSize() * 2.0f + 1.0f);
-                executor.doExecution(0);
+        mActivityTestRule.getScenario().onActivity(activity -> {
+            final AppCompatTextView tv = activity.findViewById(R.id.textview_set_text_async);
+            tv.setText(""); // Make the measured width to be zero.
+            tv.setTextFuture(PrecomputedTextCompat.getTextFuture(
+                    SAMPLE_TEXT_1, tv.getTextMetricsParamsCompat(), executor));
+            tv.setTextSize(tv.getTextSize() * 2.0f + 1.0f);
+            executor.doExecution(0);
 
-                // setText may wrap the given text with SpannedString. Check the contents by casting
-                // to String.
-                try {
-                    tv.getText();
-                    fail();
-                } catch (IllegalArgumentException e) {
-                    // pass
-                }
+            // setText may wrap the given text with SpannedString. Check the contents by casting
+            // to String.
+            try {
+                tv.getText();
+                fail();
+            } catch (IllegalArgumentException e) {
+                // pass
             }
         });
     }
@@ -929,7 +892,7 @@
     @SdkSuppress(minSdkVersion = 26)
     @Test
     public void testSetTextClassifier() {
-        final AppCompatTextView textview = new AppCompatTextView(mActivityTestRule.getActivity());
+        final AppCompatTextView textview = new AppCompatTextView(mActivity);
         NoOpTextClassifier noOpTextClassifier = new NoOpTextClassifier();
 
         textview.setTextClassifier(noOpTextClassifier);
@@ -937,6 +900,7 @@
         assertEquals(noOpTextClassifier, textview.getTextClassifier());
     }
 
+    @RequiresApi(26)
     private static class NoOpTextClassifier implements TextClassifier {}
 
     class TestCase {
diff --git a/bluetooth/integration-tests/testapp/src/main/AndroidManifest.xml b/bluetooth/integration-tests/testapp/src/main/AndroidManifest.xml
index c48ff97..f6c02c5 100644
--- a/bluetooth/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/bluetooth/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -22,5 +22,6 @@
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 
     <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
 
 </manifest>
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/MainActivity.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/MainActivity.kt
index a5d149b..3e27ff9 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/MainActivity.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/MainActivity.kt
@@ -61,8 +61,9 @@
 
         requestBluetoothPermissions.launch(
             arrayOf(
+                Manifest.permission.ACCESS_FINE_LOCATION,
+                Manifest.permission.BLUETOOTH_ADVERTISE,
                 Manifest.permission.BLUETOOTH_SCAN,
-                Manifest.permission.ACCESS_FINE_LOCATION
             )
         )
     }
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/framework/FwkFragment.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/framework/FwkFragment.kt
index 220061d..5261a18 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/framework/FwkFragment.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/framework/FwkFragment.kt
@@ -18,11 +18,15 @@
 
 import android.annotation.SuppressLint
 import android.bluetooth.BluetoothManager
+import android.bluetooth.le.AdvertiseCallback
+import android.bluetooth.le.AdvertiseData
+import android.bluetooth.le.AdvertiseSettings
 import android.bluetooth.le.ScanCallback
 import android.bluetooth.le.ScanResult
 import android.bluetooth.le.ScanSettings
 import android.content.Context
 import android.os.Bundle
+import android.os.ParcelUuid
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
@@ -37,6 +41,7 @@
 
     companion object {
         const val TAG = "FwkFragment"
+        val ServiceUUID: ParcelUuid = ParcelUuid.fromString("0000b81d-0000-1000-8000-00805f9b34fb")
     }
 
     private val scanCallback = object : ScanCallback() {
@@ -53,6 +58,16 @@
         }
     }
 
+    private val advertiseCallback = object : AdvertiseCallback() {
+        override fun onStartFailure(errorCode: Int) {
+            Log.d(TAG, "onStartFailure() called with: errorCode = $errorCode")
+        }
+
+        override fun onStartSuccess(settingsInEffect: AdvertiseSettings?) {
+            Log.d(TAG, "onStartSuccess() called")
+        }
+    }
+
     private var _binding: FragmentFwkBinding? = null
 
     // This property is only valid between onCreateView and onDestroyView.
@@ -77,6 +92,10 @@
         binding.buttonScan.setOnClickListener {
             scan()
         }
+
+        binding.switchAdvertise.setOnClickListener {
+            if (binding.switchAdvertise.isChecked) startAdvertise()
+        }
     }
 
     override fun onDestroyView() {
@@ -105,4 +124,30 @@
         Toast.makeText(context, getString(R.string.scan_start_message), Toast.LENGTH_LONG)
             .show()
     }
+
+    // Permissions are handled by MainActivity requestBluetoothPermissions
+    @SuppressLint("MissingPermission")
+    private fun startAdvertise() {
+        val bluetoothManager =
+            context?.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager
+
+        val bluetoothAdapter = bluetoothManager?.adapter
+
+        val bleAdvertiser = bluetoothAdapter?.bluetoothLeAdvertiser
+
+        val advertiseSettings = AdvertiseSettings.Builder()
+            .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
+            .setTimeout(0)
+            .build()
+
+        val advertiseData = AdvertiseData.Builder()
+            .addServiceUuid(ServiceUUID)
+            .setIncludeDeviceName(true)
+            .build()
+
+        bleAdvertiser?.startAdvertising(advertiseSettings, advertiseData, advertiseCallback)
+
+        Toast.makeText(context, getString(R.string.advertise_start_message), Toast.LENGTH_LONG)
+            .show()
+    }
 }
diff --git a/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_fwk.xml b/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_fwk.xml
index 754f93b..0b9be5f 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_fwk.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/layout/fragment_fwk.xml
@@ -34,6 +34,14 @@
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent" />
 
+    <androidx.appcompat.widget.SwitchCompat
+        android:id="@+id/switch_advertise"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/advertise_using_fwk"
+        app:layout_constraintBottom_toTopOf="@+id/button_scan"
+        app:layout_constraintEnd_toEndOf="parent" />
+
     <Button
         android:id="@+id/button_scan"
         android:layout_width="wrap_content"
diff --git a/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml b/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
index b0142e3..9828cdd 100644
--- a/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
+++ b/bluetooth/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
@@ -29,4 +29,7 @@
 
     <string name="scan_start_message">Scan should have started. Results are in Logcat</string>
     <string name="scan_not_yet_implemented">Scan not yet implemented.</string>
+
+    <string name="advertise_using_fwk">Advertise using Framework Bluetooth APIs</string>
+    <string name="advertise_start_message">Advertise started</string>
 </resources>
diff --git a/buildSrc/remoteBuildCache.gradle b/buildSrc/remoteBuildCache.gradle
index 4f3d275..9f13a47 100644
--- a/buildSrc/remoteBuildCache.gradle
+++ b/buildSrc/remoteBuildCache.gradle
@@ -17,8 +17,9 @@
 switch (cacheSetting) {
     case "true":
     case "uplink": // legacy build cache
-        throw new Exception("You are using legacy USE_ANDROIDX_REMOTE_BUILD_CACHE=$cacheSetting " +
-            "type, please move to the new cache using http://go/androidx-dev#remote-build-cache")
+        logger.warn("\u001B[31m\nYou are using legacy USE_ANDROIDX_REMOTE_BUILD_CACHE=$cacheSetting " +
+            "type, this cache has been turned down, so you are *not* using a remote cache. " +
+            "Please move to the new cache using http://go/androidx-dev#remote-build-cache\u001B[0m\n")
         break
     case "gcp":
         gradle.settingsEvaluated { settings ->
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
index fc6153b..6839558 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/CaptureSessionTest.java
@@ -75,10 +75,12 @@
 import androidx.camera.core.impl.MutableOptionsBundle;
 import androidx.camera.core.impl.Quirks;
 import androidx.camera.core.impl.SessionConfig;
+import androidx.camera.core.impl.UseCaseConfig;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.core.impl.utils.futures.FutureCallback;
 import androidx.camera.core.impl.utils.futures.Futures;
 import androidx.camera.testing.CameraUtil;
+import androidx.camera.testing.fakes.FakeUseCaseConfig;
 import androidx.concurrent.futures.CallbackToFutureAdapter;
 import androidx.core.os.HandlerCompat;
 import androidx.test.core.app.ApplicationProvider;
@@ -103,6 +105,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
@@ -231,7 +234,7 @@
         FutureCallback<Void> mockFutureCallback = mock(FutureCallback.class);
 
         Futures.addCallback(captureSession.open(mTestParameters0.mSessionConfig,
-                mCameraDeviceHolder.get(), mCaptureSessionOpenerBuilder.build()),
+                        mCameraDeviceHolder.get(), mCaptureSessionOpenerBuilder.build()),
                 mockFutureCallback, CameraXExecutors.mainThreadExecutor());
 
         assertTrue(mTestParameters0.waitForData());
@@ -277,6 +280,16 @@
                 == OutputConfigurationCompat.STREAM_USE_CASE_NONE);
     }
 
+    @SdkSuppress(maxSdkVersion = 32)
+    @Test
+    public void getStreamUseCaseFromUseCaseConfigsNotSupported() {
+        Collection<UseCaseConfig<?>> useCaseConfigs = new ArrayList<>();
+        useCaseConfigs.add(new FakeUseCaseConfig.Builder().getUseCaseConfig());
+        assertTrue(StreamUseCaseUtil.getStreamUseCaseFromUseCaseConfigs(useCaseConfigs,
+                new ArrayList<>())
+                == OutputConfigurationCompat.STREAM_USE_CASE_NONE);
+    }
+
     // Sharing surface of YUV format is supported since API 28
     @SdkSuppress(minSdkVersion = 28)
     @Test
@@ -445,7 +458,7 @@
         FutureCallback<Void> mockFutureCallback = mock(FutureCallback.class);
 
         Futures.addCallback(captureSession.open(mTestParameters0.mSessionConfig,
-                mCameraDeviceHolder.get(), mCaptureSessionOpenerBuilder.build()),
+                        mCameraDeviceHolder.get(), mCaptureSessionOpenerBuilder.build()),
                 mockFutureCallback, CameraXExecutors.mainThreadExecutor());
 
         verify(mockFutureCallback, timeout(3000)).onFailure(any(Throwable.class));
@@ -463,7 +476,7 @@
         FutureCallback<Void> mockFutureCallback = mock(FutureCallback.class);
 
         Futures.addCallback(captureSession.open(mTestParameters0.mSessionConfig,
-                mCameraDeviceHolder.get(), mCaptureSessionOpenerBuilder.build()),
+                        mCameraDeviceHolder.get(), mCaptureSessionOpenerBuilder.build()),
                 mockFutureCallback, CameraXExecutors.mainThreadExecutor());
 
         verify(mockFutureCallback, timeout(3000)).onSuccess(any());
@@ -539,7 +552,7 @@
         captureSession.setSessionConfig(parameters.mSessionConfig);
         FutureCallback<Void> mockFutureCallback = mock(FutureCallback.class);
         Futures.addCallback(captureSession.open(parameters.mSessionConfig,
-                mCameraDeviceHolder.get(), mCaptureSessionOpenerBuilder.build()),
+                        mCameraDeviceHolder.get(), mCaptureSessionOpenerBuilder.build()),
                 mockFutureCallback, CameraXExecutors.mainThreadExecutor());
 
         verify(mockFutureCallback, timeout(waitTimeout)).onSuccess(any());
@@ -1053,7 +1066,7 @@
         ArgumentCaptor<Throwable> throwableCaptor = ArgumentCaptor.forClass(Throwable.class);
 
         Futures.addCallback(captureSession.open(mTestParameters0.mSessionConfig,
-                mCameraDeviceHolder.get(), mCaptureSessionOpenerBuilder.build()),
+                        mCameraDeviceHolder.get(), mCaptureSessionOpenerBuilder.build()),
                 mockFutureCallback, CameraXExecutors.mainThreadExecutor());
 
         verify(mockFutureCallback, timeout(3000).times(1)).onFailure(throwableCaptor.capture());
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 a72ac3f..4132879 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
@@ -1122,6 +1122,9 @@
             debugLog("Unable to create capture session due to conflicting configurations");
             return;
         }
+        validatingBuilder.setStreamUseCase(StreamUseCaseUtil.getStreamUseCaseFromUseCaseConfigs(
+                mUseCaseAttachState.getAttachedUseCaseConfigs(),
+                mUseCaseAttachState.getAttachedSessionConfigs()));
 
         CaptureSessionInterface captureSession = mCaptureSession;
         ListenableFuture<Void> openCaptureSession = captureSession.open(validatingBuilder.build(),
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
index 1c30ac5..82f57d0 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
@@ -314,6 +314,7 @@
                                         outputConfig,
                                         mConfiguredSurfaceMap,
                                         physicalCameraIdForAllStreams);
+                        outputConfiguration.setStreamUseCase(sessionConfig.getStreamUseCase());
                         outputConfigList.add(outputConfiguration);
                     }
 
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java
new file mode 100644
index 0000000..4e50b76
--- /dev/null
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/StreamUseCaseUtil.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2022 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.camera2.internal;
+
+import android.hardware.camera2.CameraDevice;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.camera.camera2.internal.compat.params.OutputConfigurationCompat;
+import androidx.camera.core.impl.ImageAnalysisConfig;
+import androidx.camera.core.impl.ImageCaptureConfig;
+import androidx.camera.core.impl.PreviewConfig;
+import androidx.camera.core.impl.SessionConfig;
+import androidx.camera.core.impl.UseCaseConfig;
+import androidx.camera.core.impl.VideoCaptureConfig;
+
+import java.util.Collection;
+
+/**
+ * A class that contains utility methods for stream use case.
+ */
+public final class StreamUseCaseUtil {
+
+    private StreamUseCaseUtil() {
+    }
+
+    /**
+     * Returns the appropriate stream use case for a capture session based on the attached
+     * CameraX use cases. If API level is below 33, return
+     * {@value OutputConfigurationCompat#STREAM_USE_CASE_NONE}. If use cases are empty or is ZSL,
+     * return DEFAULT. Otherwise, return PREVIEW_VIDEO_STILL for ImageCapture + VideoCapture;
+     * return STILL_CAPTURE for ImageCapture; return VIDEO_RECORD for VideoCapture; return
+     * VIEW_FINDER for Preview only.
+     *
+     * @param useCaseConfigs collection of all attached CameraX use cases for this capture session
+     * @param sessionConfigs collection of all session configs for this capture session
+     * @return the appropriate stream use case for this capture session
+     */
+    public static long getStreamUseCaseFromUseCaseConfigs(
+            @NonNull Collection<UseCaseConfig<?>> useCaseConfigs,
+            @NonNull Collection<SessionConfig> sessionConfigs) {
+        if (Build.VERSION.SDK_INT < 33) {
+            return OutputConfigurationCompat.STREAM_USE_CASE_NONE;
+        }
+        if (useCaseConfigs.isEmpty()) {
+            //If the collection is empty, return default case.
+            //TODO: b/237337336 Return SCALER_AVAILABLE_STREAM_USE_CASE_DEFAULT here
+            return OutputConfigurationCompat.STREAM_USE_CASE_NONE;
+        } else {
+            for (SessionConfig sessionConfig : sessionConfigs) {
+                if (sessionConfig.getTemplateType() == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG) {
+                    //If is ZSL, return default case.
+                    //TODO: b/237337336 Return SCALER_AVAILABLE_STREAM_USE_CASE_DEFAULT here
+                    return OutputConfigurationCompat.STREAM_USE_CASE_NONE;
+                }
+            }
+            boolean hasImageCapture = false, hasVideoCapture = false, hasPreview = false;
+            for (UseCaseConfig<?> useCaseConfig : useCaseConfigs) {
+                if (useCaseConfig instanceof ImageAnalysisConfig) {
+                    //If contains analysis use case, return default case.
+                    //TODO: b/237337336 Return SCALER_AVAILABLE_STREAM_USE_CASE_DEFAULT here
+                    return OutputConfigurationCompat.STREAM_USE_CASE_NONE;
+                }
+
+                if (useCaseConfig instanceof PreviewConfig) {
+                    hasPreview = true;
+                    continue;
+                }
+
+                if (useCaseConfig instanceof ImageCaptureConfig) {
+                    if (hasVideoCapture) {
+                        // If has both image and video capture, return preview video still case.
+                        //TODO: b/237337336 Return
+                        // SCALER_AVAILABLE_STREAM_USE_CASE_PREVIEW_VIDEO_STILL here
+                        return OutputConfigurationCompat.STREAM_USE_CASE_NONE;
+                    }
+                    hasImageCapture = true;
+                    continue;
+
+                }
+
+                if (useCaseConfig instanceof VideoCaptureConfig) {
+                    if (hasImageCapture) {
+                        // If has both image and video capture, return preview video still case.
+                        //TODO: b/237337336 Return
+                        // SCALER_AVAILABLE_STREAM_USE_CASE_PREVIEW_VIDEO_STILL here
+                        return OutputConfigurationCompat.STREAM_USE_CASE_NONE;
+                    }
+                    hasVideoCapture = true;
+                    continue;
+
+                }
+            }
+            if (!hasPreview) {
+                // If doesn't contain preview, we are not sure what's the situation. Return
+                // default case.
+                //TODO: b/237337336 Return SCALER_AVAILABLE_STREAM_USE_CASE_DEFAULT here
+                return OutputConfigurationCompat.STREAM_USE_CASE_NONE;
+            }
+
+            if (hasImageCapture) {
+                // If contains image capture, return still capture case.
+                //TODO: b/237337336 Return SCALER_AVAILABLE_STREAM_USE_CASE_STILL_CAPTURE here
+                return OutputConfigurationCompat.STREAM_USE_CASE_NONE;
+            } else if (hasVideoCapture) {
+                // If contains video capture, return video record case.
+                //TODO: b/237337336 Return SCALER_AVAILABLE_STREAM_USE_CASE_VIDEO_RECORD here
+                return OutputConfigurationCompat.STREAM_USE_CASE_NONE;
+            } else {
+                // If contains only preview, return view finder case.
+                //TODO: b/237337336 Return SCALER_AVAILABLE_STREAM_USE_CASE_VIEW_FINDER here
+                return OutputConfigurationCompat.STREAM_USE_CASE_NONE;
+            }
+        }
+    }
+}
diff --git a/camera/camera-core/api/current.txt b/camera/camera-core/api/current.txt
index e0cfba8..9aaeb6b 100644
--- a/camera/camera-core/api/current.txt
+++ b/camera/camera-core/api/current.txt
@@ -193,8 +193,8 @@
 
   public static interface ImageAnalysis.Analyzer {
     method public void analyze(androidx.camera.core.ImageProxy);
+    method public default android.util.Size? getDefaultTargetResolution();
     method public default int getTargetCoordinateSystem();
-    method public default android.util.Size? getTargetResolutionOverride();
     method public default void updateTransform(android.graphics.Matrix?);
   }
 
diff --git a/camera/camera-core/api/public_plus_experimental_current.txt b/camera/camera-core/api/public_plus_experimental_current.txt
index 67ac528..613a450 100644
--- a/camera/camera-core/api/public_plus_experimental_current.txt
+++ b/camera/camera-core/api/public_plus_experimental_current.txt
@@ -204,8 +204,8 @@
 
   public static interface ImageAnalysis.Analyzer {
     method public void analyze(androidx.camera.core.ImageProxy);
+    method public default android.util.Size? getDefaultTargetResolution();
     method public default int getTargetCoordinateSystem();
-    method public default android.util.Size? getTargetResolutionOverride();
     method public default void updateTransform(android.graphics.Matrix?);
   }
 
diff --git a/camera/camera-core/api/restricted_current.txt b/camera/camera-core/api/restricted_current.txt
index e0cfba8..9aaeb6b 100644
--- a/camera/camera-core/api/restricted_current.txt
+++ b/camera/camera-core/api/restricted_current.txt
@@ -193,8 +193,8 @@
 
   public static interface ImageAnalysis.Analyzer {
     method public void analyze(androidx.camera.core.ImageProxy);
+    method public default android.util.Size? getDefaultTargetResolution();
     method public default int getTargetCoordinateSystem();
-    method public default android.util.Size? getTargetResolutionOverride();
     method public default void updateTransform(android.graphics.Matrix?);
   }
 
diff --git a/camera/camera-core/build.gradle b/camera/camera-core/build.gradle
index c11aab9..a266a1e 100644
--- a/camera/camera-core/build.gradle
+++ b/camera/camera-core/build.gradle
@@ -34,6 +34,7 @@
     implementation("androidx.concurrent:concurrent-futures:1.0.0")
     implementation("androidx.lifecycle:lifecycle-common:2.1.0")
     implementation(libs.autoValueAnnotations)
+    androidTestImplementation project(path: ':camera:camera-camera2')
     compileOnly(project(":external:libyuv"))
 
     annotationProcessor(libs.autoValue)
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/SessionConfigTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/SessionConfigTest.java
index 38b2fd2..b283a24 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/SessionConfigTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/SessionConfigTest.java
@@ -26,6 +26,7 @@
 import android.hardware.camera2.CameraDevice;
 import android.view.Surface;
 
+import androidx.camera.camera2.internal.compat.params.OutputConfigurationCompat;
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.Preview;
 import androidx.camera.core.VideoCapture;
@@ -284,6 +285,15 @@
     }
 
     @Test
+    public void setStreamUseCase() {
+        SessionConfig.ValidatingBuilder validatingBuilder = new SessionConfig.ValidatingBuilder();
+        assertThat(validatingBuilder.build().getStreamUseCase()
+                == OutputConfigurationCompat.STREAM_USE_CASE_NONE);
+        validatingBuilder.setStreamUseCase(1);
+        assertThat(validatingBuilder.build().getStreamUseCase() == 1);
+    }
+
+    @Test
     public void conflictingOptions() {
         SessionConfig.Builder builder0 = new SessionConfig.Builder();
         MutableOptionsBundle options0 = MutableOptionsBundle.create();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
index b2ac134..b7caa7b 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
@@ -256,9 +256,11 @@
         Size analyzerResolution;
         synchronized (mAnalysisLock) {
             analyzerResolution = mSubscribedAnalyzer != null
-                    ? mSubscribedAnalyzer.getTargetResolutionOverride() : null;
+                    ? mSubscribedAnalyzer.getDefaultTargetResolution() : null;
         }
-        if (analyzerResolution != null) {
+
+        if (analyzerResolution != null
+                && !builder.getUseCaseConfig().containsOption(OPTION_TARGET_RESOLUTION)) {
             builder.getMutableConfig().insertOption(OPTION_TARGET_RESOLUTION, analyzerResolution);
         }
 
@@ -794,22 +796,27 @@
         void analyze(@NonNull ImageProxy image);
 
         /**
-         * Implement this method to override the target resolution of {@link ImageAnalysis}.
+         * Implement this method to set a default target resolution for the {@link ImageAnalysis}.
          *
          * <p> Implement this method if the {@link Analyzer} requires a specific resolution to
-         * work. The return value will be used to override the target resolution of the
-         * {@link ImageAnalysis}. Return {@code null} if no overriding is needed. By default,
+         * work. The return value will be used as the default target resolution for the
+         * {@link ImageAnalysis}. Return {@code null} if no falling back is needed. By default,
          * this method returns {@code null}.
          *
+         * <p> If the app does not set a target resolution for {@link ImageAnalysis}, then this
+         * value will be used as the target resolution. If the {@link ImageAnalysis} has set a
+         * target resolution, e.g. if {@link ImageAnalysis.Builder#setTargetResolution(Size)} is
+         * called, then the {@link ImageAnalysis} will use the app value over this value.
+         *
          * <p> Note that this method is invoked by CameraX at the time of binding to lifecycle. In
          * order for this value to be effective, the {@link Analyzer} has to be set before
          * {@link ImageAnalysis} is bound to a lifecycle. Otherwise, the value will be ignored.
          *
-         * @return the resolution to override the target resolution of {@link ImageAnalysis}, or
-         * {@code null} if no overriding is needed.
+         * @return the default resolution of {@link ImageAnalysis}, or {@code null} if no specific
+         * resolution is needed.
          */
         @Nullable
-        default Size getTargetResolutionOverride() {
+        default Size getDefaultTargetResolution() {
             return null;
         }
 
@@ -842,10 +849,16 @@
          * by the implementation to transform the coordinates detected in the camera frame. For
          * example, the coordinates of the detected face.
          *
-         * <p>If the value is {@code null}, it could either mean value of the target coordinate
-         * system is {@link #COORDINATE_SYSTEM_ORIGINAL}, or currently there is no valid
-         * transformation for the target coordinate system, for example, if currently the view
-         * finder is not visible in UI.
+         * <p>If the value is {@code null}, it means that no valid transformation is available.
+         * It could have different causes depending on the value of
+         * {@link #getTargetCoordinateSystem()}:
+         * <ul>
+         *     <li> If the target coordinate system is {@link #COORDINATE_SYSTEM_ORIGINAL}, it is
+         *     always invalid because in that case, the coordinate system depends on how the
+         *     analysis algorithm processes the {@link ImageProxy}.
+         *     <li> It is also invalid if the target coordinate system is not available, for example
+         *     if the analyzer targets the viewfinder and the view finder is not visible in UI.
+         * </ul>
          *
          * <p>This method is invoked whenever a new transformation is ready. For example, when
          * the view finder is first a launched as well as when it's resized.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java
index 73747cf..1620e3d 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java
@@ -47,6 +47,9 @@
  */
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 public final class SessionConfig {
+    /** The value represents an unset or unsupported stream use case. */
+    private static final long STREAM_USE_CASE_NONE = -1;
+
     /** The set of {@link OutputConfig} that data from the camera will be put into. */
     private final List<OutputConfig> mOutputConfigs;
     /** The state callback for a {@link CameraDevice}. */
@@ -58,6 +61,8 @@
     private final List<ErrorListener> mErrorListeners;
     /** The configuration for building the {@link CaptureRequest} used for repeating requests. */
     private final CaptureConfig mRepeatingCaptureConfig;
+    /** The stream use case for the capture session. */
+    private final long mStreamUseCase;
 
     /**
      * Immutable class to store an input configuration that is used to create a reprocessable
@@ -173,6 +178,7 @@
      * @param repeatingCaptureConfig The configuration for building the {@link CaptureRequest}.
      * @param inputConfiguration     The input configuration to create a reprocessable capture
      *                               session.
+     * @param streamUseCase          The value of stream use case for the capture session.
      */
     SessionConfig(
             List<OutputConfig> outputConfigs,
@@ -181,7 +187,8 @@
             List<CameraCaptureCallback> singleCameraCaptureCallbacks,
             List<ErrorListener> errorListeners,
             CaptureConfig repeatingCaptureConfig,
-            @Nullable InputConfiguration inputConfiguration) {
+            @Nullable InputConfiguration inputConfiguration,
+            long streamUseCase) {
         mOutputConfigs = outputConfigs;
         mDeviceStateCallbacks = Collections.unmodifiableList(deviceStateCallbacks);
         mSessionStateCallbacks = Collections.unmodifiableList(sessionStateCallbacks);
@@ -190,6 +197,7 @@
         mErrorListeners = Collections.unmodifiableList(errorListeners);
         mRepeatingCaptureConfig = repeatingCaptureConfig;
         mInputConfiguration = inputConfiguration;
+        mStreamUseCase = streamUseCase;
     }
 
     /** Returns an instance of a session configuration with minimal configurations. */
@@ -202,7 +210,8 @@
                 new ArrayList<CameraCaptureCallback>(0),
                 new ArrayList<>(0),
                 new CaptureConfig.Builder().build(),
-                /* inputConfiguration */ null);
+                /* inputConfiguration */ null,
+                /* streamUseCase */ STREAM_USE_CASE_NONE);
     }
 
     @Nullable
@@ -241,6 +250,10 @@
         return mRepeatingCaptureConfig.getTemplateType();
     }
 
+    public long getStreamUseCase() {
+        return mStreamUseCase;
+    }
+
     /** Obtains all registered {@link CameraDevice.StateCallback} callbacks. */
     @NonNull
     public List<CameraDevice.StateCallback> getDeviceStateCallbacks() {
@@ -614,7 +627,8 @@
                     mSingleCameraCaptureCallbacks,
                     mErrorListeners,
                     mCaptureConfigBuilder.build(),
-                    mInputConfiguration);
+                    mInputConfiguration,
+                    /* streamUseCase */ STREAM_USE_CASE_NONE);
         }
     }
 
@@ -637,6 +651,11 @@
         private final SurfaceSorter mSurfaceSorter = new SurfaceSorter();
         private boolean mValid = true;
         private boolean mTemplateSet = false;
+        private long mStreamUseCase = STREAM_USE_CASE_NONE;
+
+        public void setStreamUseCase(long streamUseCase) {
+            mStreamUseCase = streamUseCase;
+        }
 
         /**
          * Add the SessionConfig to the set of SessionConfig that have been aggregated by the
@@ -738,7 +757,8 @@
                     mSingleCameraCaptureCallbacks,
                     mErrorListeners,
                     mCaptureConfigBuilder.build(),
-                    mInputConfiguration);
+                    mInputConfiguration,
+                    mStreamUseCase);
         }
 
         private int selectTemplateType(int type1, int type2) {
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 bdce064..9cb4ec3e 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
@@ -30,6 +30,7 @@
 import android.view.Surface;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.camera.core.impl.CameraFactory;
 import androidx.camera.core.impl.CameraInternal;
 import androidx.camera.core.impl.ImageAnalysisConfig;
@@ -72,6 +73,9 @@
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 public class ImageAnalysisTest {
 
+    private static final Size APP_RESOLUTION = new Size(100, 200);
+    private static final Size ANALYZER_RESOLUTION = new Size(300, 400);
+
     private static final int QUEUE_DEPTH = 8;
     private static final int IMAGE_TAG = 0;
     private static final long TIMESTAMP_1 = 1;
@@ -135,15 +139,33 @@
     }
 
     @Test
-    public void setAnalyzerWithResolution_overridesUseCaseResolution() {
+    public void setAnalyzerWithResolution_doesNotOverridesUseCaseResolution() {
+        assertThat(getMergedAnalyzerResolution(APP_RESOLUTION, ANALYZER_RESOLUTION)).isEqualTo(
+                APP_RESOLUTION);
+    }
+
+    @Test
+    public void setAnalyzerWithResolution_usedAsDefaultUseCaseResolution() {
+        assertThat(getMergedAnalyzerResolution(null, ANALYZER_RESOLUTION)).isEqualTo(
+                ANALYZER_RESOLUTION);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void noAppOrAnalyzerResolution_noMergedOption() {
+        getMergedAnalyzerResolution(null, null);
+    }
+
+    @NonNull
+    private Size getMergedAnalyzerResolution(
+            @Nullable Size appResolution,
+            @Nullable Size analyzerResolution) {
         // Arrange: set up ImageAnalysis with target resolution.
-        Size appResolution = new Size(100, 200);
-        ImageAnalysis.Builder builder =
-                new ImageAnalysis.Builder().setTargetResolution(appResolution).setImageQueueDepth(
-                        QUEUE_DEPTH);
+        ImageAnalysis.Builder builder = new ImageAnalysis.Builder().setImageQueueDepth(QUEUE_DEPTH);
+        if (appResolution != null) {
+            builder.setTargetResolution(appResolution);
+        }
         mImageAnalysis = builder.build();
         // Analyzer that overrides the resolution.
-        Size analyzerResolution = new Size(300, 400);
         ImageAnalysis.Analyzer analyzer = new ImageAnalysis.Analyzer() {
             @Override
             public void analyze(@NonNull ImageProxy image) {
@@ -151,7 +173,7 @@
             }
 
             @Override
-            public Size getTargetResolutionOverride() {
+            public Size getDefaultTargetResolution() {
                 return analyzerResolution;
             }
         };
@@ -162,8 +184,9 @@
         // Assert: only the target resolution is overridden.
         ImageAnalysisConfig mergedConfig = (ImageAnalysisConfig) mImageAnalysis.mergeConfigs(
                 new FakeCameraInfoInternal(), null, null);
-        assertThat(mergedConfig.getTargetResolution()).isEqualTo(analyzerResolution);
+
         assertThat(mergedConfig.getImageQueueDepth()).isEqualTo(QUEUE_DEPTH);
+        return mergedConfig.getTargetResolution();
     }
 
     @Test
diff --git a/camera/camera-mlkit-vision/api/current.txt b/camera/camera-mlkit-vision/api/current.txt
index 5456107..0599c25 100644
--- a/camera/camera-mlkit-vision/api/current.txt
+++ b/camera/camera-mlkit-vision/api/current.txt
@@ -4,8 +4,8 @@
   @RequiresApi(21) public class MlKitAnalyzer implements androidx.camera.core.ImageAnalysis.Analyzer {
     ctor public MlKitAnalyzer(java.util.List<com.google.mlkit.vision.interfaces.Detector<?>!>, int, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.mlkit.vision.MlKitAnalyzer.Result!>);
     method public final void analyze(androidx.camera.core.ImageProxy);
+    method public final android.util.Size getDefaultTargetResolution();
     method public final int getTargetCoordinateSystem();
-    method public final android.util.Size getTargetResolutionOverride();
     method public final void updateTransform(android.graphics.Matrix?);
   }
 
diff --git a/camera/camera-mlkit-vision/api/public_plus_experimental_current.txt b/camera/camera-mlkit-vision/api/public_plus_experimental_current.txt
index 5456107..0599c25 100644
--- a/camera/camera-mlkit-vision/api/public_plus_experimental_current.txt
+++ b/camera/camera-mlkit-vision/api/public_plus_experimental_current.txt
@@ -4,8 +4,8 @@
   @RequiresApi(21) public class MlKitAnalyzer implements androidx.camera.core.ImageAnalysis.Analyzer {
     ctor public MlKitAnalyzer(java.util.List<com.google.mlkit.vision.interfaces.Detector<?>!>, int, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.mlkit.vision.MlKitAnalyzer.Result!>);
     method public final void analyze(androidx.camera.core.ImageProxy);
+    method public final android.util.Size getDefaultTargetResolution();
     method public final int getTargetCoordinateSystem();
-    method public final android.util.Size getTargetResolutionOverride();
     method public final void updateTransform(android.graphics.Matrix?);
   }
 
diff --git a/camera/camera-mlkit-vision/api/restricted_current.txt b/camera/camera-mlkit-vision/api/restricted_current.txt
index 5456107..0599c25 100644
--- a/camera/camera-mlkit-vision/api/restricted_current.txt
+++ b/camera/camera-mlkit-vision/api/restricted_current.txt
@@ -4,8 +4,8 @@
   @RequiresApi(21) public class MlKitAnalyzer implements androidx.camera.core.ImageAnalysis.Analyzer {
     ctor public MlKitAnalyzer(java.util.List<com.google.mlkit.vision.interfaces.Detector<?>!>, int, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.mlkit.vision.MlKitAnalyzer.Result!>);
     method public final void analyze(androidx.camera.core.ImageProxy);
+    method public final android.util.Size getDefaultTargetResolution();
     method public final int getTargetCoordinateSystem();
-    method public final android.util.Size getTargetResolutionOverride();
     method public final void updateTransform(android.graphics.Matrix?);
   }
 
diff --git a/camera/camera-mlkit-vision/src/main/java/androidx/camera/mlkit/vision/MlKitAnalyzer.java b/camera/camera-mlkit-vision/src/main/java/androidx/camera/mlkit/vision/MlKitAnalyzer.java
index 66449f8..3ae7d6b 100644
--- a/camera/camera-mlkit-vision/src/main/java/androidx/camera/mlkit/vision/MlKitAnalyzer.java
+++ b/camera/camera-mlkit-vision/src/main/java/androidx/camera/mlkit/vision/MlKitAnalyzer.java
@@ -56,13 +56,19 @@
  * {@code Detector}s finish analyzing the frame, {@link Consumer#accept} will be
  * invoked with the aggregated analysis results.
  *
+ * <p> This class handles the coordinates transformation between MLKit output and the target
+ * coordinate system. Based the {@code targetCoordinateSystem} set in the constructor, it
+ * calculates the {@link Matrix} with the value provided by CameraX via
+ * {@link ImageAnalysis.Analyzer#updateTransform} and forward it to the MLKit {@code Detector}. The
+ * coordinates returned by MLKit will be in the desired coordinate system.
+ *
  * <p> This class is designed to work seamlessly with the {@code CameraController} class in
  * camera-view. When used with {@link ImageAnalysis} in camera-core, the following scenarios may
  * need special handling:
  * <ul>
  * <li> Cannot transform coordinates to UI coordinate system. e.g. camera-core only supports
  * {@link ImageAnalysis#COORDINATE_SYSTEM_ORIGINAL}.
- * <li>For the value of {@link #getTargetResolutionOverride()} to be effective, make sure
+ * <li>For the value of {@link #getDefaultTargetResolution()} to be effective, make sure
  * the {@link ImageAnalysis#setAnalyzer} is called before it's bound to the lifecycle.
  * </ul>
  *
@@ -112,6 +118,12 @@
      * {@code Detector#getDetectorType()} is TYPE_SEGMENTATION and {@code targetCoordinateSystem}
      * is COORDINATE_SYSTEM_ORIGINAL. Currently MLKit does not support transformation with
      * segmentation.
+     *
+     * @param detectors              list of MLKit {@link Detector}.
+     * @param targetCoordinateSystem e.g. {@link ImageAnalysis#COORDINATE_SYSTEM_ORIGINAL}
+     *                               the coordinates in MLKit output will be based on this value.
+     * @param executor               on which the consumer is invoked.
+     * @param consumer               invoked when there is new MLKit result.
      */
     @OptIn(markerClass = TransformExperimental.class)
     public MlKitAnalyzer(
@@ -214,7 +226,7 @@
      */
     @NonNull
     @Override
-    public final Size getTargetResolutionOverride() {
+    public final Size getDefaultTargetResolution() {
         Size size = DEFAULT_SIZE;
         for (Detector<?> detector : mDetectors) {
             Size detectorSize = getTargetResolution(detector.getDetectorType());
diff --git a/camera/camera-mlkit-vision/src/test/java/androidx/camera/mlkit/vision/MlKitAnalyzerTest.kt b/camera/camera-mlkit-vision/src/test/java/androidx/camera/mlkit/vision/MlKitAnalyzerTest.kt
index 96cee82..23cd954 100644
--- a/camera/camera-mlkit-vision/src/test/java/androidx/camera/mlkit/vision/MlKitAnalyzerTest.kt
+++ b/camera/camera-mlkit-vision/src/test/java/androidx/camera/mlkit/vision/MlKitAnalyzerTest.kt
@@ -63,7 +63,7 @@
             directExecutor()
         ) {}
 
-        assertThat(mlKitAnalyzer.targetResolutionOverride).isEqualTo(Size(1280, 720))
+        assertThat(mlKitAnalyzer.defaultTargetResolution).isEqualTo(Size(1280, 720))
     }
 
     @Test
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/AudioChecker.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/AudioChecker.kt
new file mode 100644
index 0000000..dbd3371
--- /dev/null
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/AudioChecker.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2022 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.video
+
+import android.content.Context
+import androidx.annotation.RequiresApi
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.Logger
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.testing.CameraUtil
+import androidx.camera.video.internal.AudioSource
+import androidx.camera.video.internal.FakeBufferProvider
+import androidx.camera.video.internal.config.AudioSourceSettingsCamcorderProfileResolver
+import androidx.camera.video.internal.encoder.FakeInputBuffer
+import androidx.concurrent.futures.await
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.runBlocking
+
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class AudioChecker {
+
+    companion object {
+        private const val TAG = "AudioChecker"
+
+        fun canAudioSourceBeStarted(
+            context: Context,
+            cameraSelector: CameraSelector,
+            qualitySelector: QualitySelector
+        ): Boolean {
+            return try {
+                checkAudioSourceCanBeStarted(context, cameraSelector, qualitySelector)
+                Logger.i(TAG, "Audio source can be started.")
+                true
+            } catch (t: Throwable) {
+                Logger.i(TAG, "Audio source failed to start.", t)
+                false
+            }
+        }
+
+        private fun checkAudioSourceCanBeStarted(
+            context: Context,
+            cameraSelector: CameraSelector,
+            qualitySelector: QualitySelector
+        ) = runBlocking {
+            // Get audio source settings from CamcorderProfile
+            val cameraInfo =
+                CameraUtil.createCameraUseCaseAdapter(context, cameraSelector).cameraInfo
+            val videoCapabilities = VideoCapabilities.from(cameraInfo)
+            val quality = qualitySelector.getPrioritizedQualities(cameraInfo).first()
+            // Get a config using the default audio spec.
+            val audioSourceSettings =
+                AudioSourceSettingsCamcorderProfileResolver(
+                    AudioSpec.builder().build(),
+                    videoCapabilities.getProfile(quality)!!
+                ).get()
+            val audioSource = AudioSource(audioSourceSettings, CameraXExecutors.ioExecutor(), null)
+            try {
+                val completable = CompletableDeferred<Any?>()
+                audioSource.setAudioSourceCallback(CameraXExecutors.directExecutor(),
+                    object : AudioSource.AudioSourceCallback {
+                        override fun onSilenced(silenced: Boolean) {
+                            // Ignore
+                        }
+
+                        override fun onError(t: Throwable) {
+                            completable.completeExceptionally(t)
+                        }
+                    })
+
+                val fakeBufferProvider = FakeBufferProvider {
+                    completable.complete(null)
+                    FakeInputBuffer()
+                }
+                audioSource.setBufferProvider(fakeBufferProvider)
+                fakeBufferProvider.setActive(true)
+                audioSource.start()
+                completable.await()
+            } finally {
+                audioSource.release().await()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
index 84c6554..a75dd4a 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
@@ -40,6 +40,7 @@
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.SurfaceTextureProvider
 import androidx.camera.testing.fakes.FakeLifecycleOwner
+import androidx.camera.video.VideoRecordEvent.Finalize.ERROR_NONE
 import androidx.camera.video.VideoRecordEvent.Finalize.ERROR_SOURCE_INACTIVE
 import androidx.core.util.Consumer
 import androidx.test.core.app.ApplicationProvider
@@ -53,7 +54,8 @@
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
 import org.junit.After
-import org.junit.Assume
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -87,6 +89,7 @@
     companion object {
         private const val VIDEO_TIMEOUT_SEC = 10L
         private const val TAG = "VideoRecordingTest"
+
         @JvmStatic
         @Parameterized.Parameters
         fun data(): Collection<Array<Any>> {
@@ -110,6 +113,12 @@
 
     private lateinit var finalize: VideoRecordEvent.Finalize
 
+    private val audioSourceAvailable by lazy {
+        AudioChecker.canAudioSourceBeStarted(
+            context, cameraSelector, Recorder.DEFAULT_QUALITY_SELECTOR
+        )
+    }
+
     private val videoRecordEventListener = Consumer<VideoRecordEvent> {
         when (it) {
             is VideoRecordEvent.Start -> {
@@ -137,9 +146,9 @@
 
     @Before
     fun setUp() {
-        Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(cameraSelector.lensFacing!!))
+        assumeTrue(CameraUtil.hasCameraWithLensFacing(cameraSelector.lensFacing!!))
         // Skip for b/168175357, b/233661493
-        Assume.assumeFalse(
+        assumeFalse(
             "Skip tests for Cuttlefish MediaCodec issues",
             Build.MODEL.contains("Cuttlefish") &&
                 (Build.VERSION.SDK_INT == 29 || Build.VERSION.SDK_INT == 33)
@@ -202,7 +211,7 @@
     @Test
     fun getCorrectResolution_when_setSupportedQuality() {
         // Pre-arrange.
-        Assume.assumeTrue(QualitySelector.getSupportedQualities(cameraInfo).isNotEmpty())
+        assumeTrue(QualitySelector.getSupportedQualities(cameraInfo).isNotEmpty())
         val qualityList = QualitySelector.getSupportedQualities(cameraInfo)
         Log.d(TAG, "CameraSelector: ${cameraSelector.lensFacing}, QualityList: $qualityList ")
 
@@ -315,6 +324,7 @@
         // Arrange.
         val videoCapture = VideoCapture.withOutput(Recorder.Builder().build())
         val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
+
         @Suppress("UNCHECKED_CAST")
         val mockListener = mock(Consumer::class.java) as Consumer<VideoRecordEvent>
         instrumentation.runOnMainSync {
@@ -346,7 +356,7 @@
         // Pre-check and arrange
         val videoCapture = VideoCapture.withOutput(Recorder.Builder().build())
         val analysis = ImageAnalysis.Builder().build()
-        Assume.assumeTrue(camera.isUseCasesCombinationSupported(preview, videoCapture, analysis))
+        assumeTrue(camera.isUseCasesCombinationSupported(preview, videoCapture, analysis))
 
         val file = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
         latchForVideoSaved = CountDownLatch(1)
@@ -382,7 +392,7 @@
         // Pre-check and arrange
         val videoCapture = VideoCapture.withOutput(Recorder.Builder().build())
         val imageCapture = ImageCapture.Builder().build()
-        Assume.assumeTrue(
+        assumeTrue(
             camera.isUseCasesCombinationSupported(
                 preview,
                 videoCapture,
@@ -423,67 +433,22 @@
 
     @Test
     fun canRecordMultipleFilesInARow() {
-        @Suppress("UNCHECKED_CAST")
-        val mockListener = mock(Consumer::class.java) as Consumer<VideoRecordEvent>
         val videoCapture = VideoCapture.withOutput(Recorder.Builder().build())
         instrumentation.runOnMainSync {
             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, videoCapture)
         }
         val file1 = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
-
-        val inOrder = inOrder(mockListener)
-        videoCapture.output.prepareRecording(context, FileOutputOptions.Builder(file1).build())
-            .withAudioEnabled()
-            .start(CameraXExecutors.directExecutor(), mockListener).use { activeRecording ->
-
-                inOrder.verify(mockListener, timeout(5000L))
-                    .accept(any(VideoRecordEvent.Start::class.java))
-                inOrder.verify(mockListener, timeout(15000L).atLeast(5))
-                    .accept(any(VideoRecordEvent.Status::class.java))
-
-                activeRecording.stop()
-            }
-
-        inOrder.verify(mockListener, timeout(5000L))
-            .accept(any(VideoRecordEvent.Finalize::class.java))
+        performRecording(videoCapture, file1, includeAudio = audioSourceAvailable)
 
         val file2 = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
-
-        videoCapture.output.prepareRecording(context, FileOutputOptions.Builder(file2).build())
-            .withAudioEnabled()
-            .start(CameraXExecutors.directExecutor(), mockListener).use { activeRecording ->
-
-                inOrder.verify(mockListener, timeout(5000L))
-                    .accept(any(VideoRecordEvent.Start::class.java))
-                inOrder.verify(mockListener, timeout(15000L).atLeast(5))
-                    .accept(any(VideoRecordEvent.Status::class.java))
-
-                activeRecording.stop()
-            }
-
-        inOrder.verify(mockListener, timeout(5000L))
-            .accept(any(VideoRecordEvent.Finalize::class.java))
+        performRecording(videoCapture, file2, includeAudio = audioSourceAvailable)
 
         val file3 = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
+        performRecording(videoCapture, file3, includeAudio = audioSourceAvailable)
 
-        videoCapture.output.prepareRecording(context, FileOutputOptions.Builder(file3).build())
-            .withAudioEnabled()
-            .start(CameraXExecutors.directExecutor(), mockListener).use { activeRecording ->
-
-                inOrder.verify(mockListener, timeout(5000L))
-                    .accept(any(VideoRecordEvent.Start::class.java))
-                inOrder.verify(mockListener, timeout(15000L).atLeast(5))
-                    .accept(any(VideoRecordEvent.Status::class.java))
-
-                activeRecording.stop()
-            }
-
-        inOrder.verify(mockListener, timeout(5000L))
-            .accept(any(VideoRecordEvent.Finalize::class.java))
-
-        verifyRecordingResult(file1, true)
-        verifyRecordingResult(file2, true)
-        verifyRecordingResult(file3, true)
+        verifyRecordingResult(file1, audioSourceAvailable)
+        verifyRecordingResult(file2, audioSourceAvailable)
+        verifyRecordingResult(file3, audioSourceAvailable)
 
         file1.delete()
         file2.delete()
@@ -491,40 +456,70 @@
     }
 
     @Test
+    fun canRecordMultipleFilesWithThenWithoutAudio() {
+        // This test requires that audio is available
+        assumeTrue(audioSourceAvailable)
+        val videoCapture = VideoCapture.withOutput(Recorder.Builder().build())
+        instrumentation.runOnMainSync {
+            cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, videoCapture)
+        }
+        val file1 = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
+        performRecording(videoCapture, file1, includeAudio = true)
+
+        val file2 = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
+        performRecording(videoCapture, file2, includeAudio = false)
+
+        verifyRecordingResult(file1, true)
+        verifyRecordingResult(file2, false)
+
+        file1.delete()
+        file2.delete()
+    }
+
+    @Test
+    fun canRecordMultipleFilesWithoutThenWithAudio() {
+        // This test requires that audio is available
+        assumeTrue(audioSourceAvailable)
+        val videoCapture = VideoCapture.withOutput(Recorder.Builder().build())
+        instrumentation.runOnMainSync {
+            cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, videoCapture)
+        }
+        val file1 = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
+        performRecording(videoCapture, file1, includeAudio = false)
+
+        val file2 = File.createTempFile("CameraX", ".tmp").apply { deleteOnExit() }
+        performRecording(videoCapture, file2, includeAudio = true)
+
+        verifyRecordingResult(file1, false)
+        verifyRecordingResult(file2, true)
+
+        file1.delete()
+        file2.delete()
+    }
+
+    @Test
     fun canStartNextRecordingPausedAfterFirstRecordingFinalized() {
-        @Suppress("UNCHECKED_CAST")
-        val mockListener = mock(Consumer::class.java) as Consumer<VideoRecordEvent>
         val videoCapture = VideoCapture.withOutput(Recorder.Builder().build())
         instrumentation.runOnMainSync {
             cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, videoCapture)
         }
 
-        val file1 = File.createTempFile("CameraX1", ".tmp").apply { deleteOnExit() }
-        val file2 = File.createTempFile("CameraX2", ".tmp").apply { deleteOnExit() }
-
         // Start and stop a recording to ensure recorder is idling
-        val inOrder = inOrder(mockListener)
+        val file1 = File.createTempFile("CameraX1", ".tmp").apply { deleteOnExit() }
 
-        videoCapture.output.prepareRecording(context, FileOutputOptions.Builder(file1).build())
-            .withAudioEnabled()
-            .start(CameraXExecutors.directExecutor(), mockListener).use { activeRecording1 ->
-
-                inOrder.verify(mockListener, timeout(5000L))
-                    .accept(any(VideoRecordEvent.Start::class.java))
-
-                inOrder.verify(mockListener, timeout(15000L).atLeast(5))
-                    .accept(any(VideoRecordEvent.Status::class.java))
-
-                activeRecording1.stop()
-            }
-
-        inOrder.verify(mockListener, timeout(5000L))
-            .accept(any(VideoRecordEvent.Finalize::class.java))
+        performRecording(videoCapture, file1, audioSourceAvailable)
 
         // First recording is now finalized. Try starting second recording paused.
+        @Suppress("UNCHECKED_CAST")
+        val mockListener = mock(Consumer::class.java) as Consumer<VideoRecordEvent>
+        val inOrder = inOrder(mockListener)
+        val file2 = File.createTempFile("CameraX2", ".tmp").apply { deleteOnExit() }
         videoCapture.output.prepareRecording(context, FileOutputOptions.Builder(file2).build())
-            .withAudioEnabled()
-            .start(CameraXExecutors.directExecutor(), mockListener).use { activeRecording2 ->
+            .apply {
+                if (audioSourceAvailable) {
+                    withAudioEnabled()
+                }
+            }.start(CameraXExecutors.directExecutor(), mockListener).use { activeRecording2 ->
 
                 activeRecording2.pause()
 
@@ -585,6 +580,7 @@
 
     @Test
     fun canSwitchAudioOnOff() {
+        assumeTrue("Audio source is not available", audioSourceAvailable)
         @Suppress("UNCHECKED_CAST")
         val mockListener = mock(Consumer::class.java) as Consumer<VideoRecordEvent>
         val videoCapture = VideoCapture.withOutput(Recorder.Builder().build())
@@ -653,23 +649,78 @@
         }
     }
 
-    private fun startVideoRecording(videoCapture: VideoCapture<Recorder>, file: File):
-        Recording {
-            val recording = videoCapture.output
-                .prepareRecording(context, FileOutputOptions.Builder(file).build())
-                .start(CameraXExecutors.directExecutor(), videoRecordEventListener)
+    private fun performRecording(
+        videoCapture: VideoCapture<Recorder>,
+        file: File,
+        includeAudio: Boolean = false
+    ) {
+        @Suppress("UNCHECKED_CAST")
+        val mockListener = mock(Consumer::class.java) as Consumer<VideoRecordEvent>
+        val inOrder = inOrder(mockListener)
+        videoCapture.output.prepareRecording(context, FileOutputOptions.Builder(file).build())
+            .apply {
+                if (includeAudio) {
+                    withAudioEnabled()
+                }
+            }
+            .start(CameraXExecutors.directExecutor(), mockListener).use { activeRecording ->
 
-            try {
-                // Wait for status event to proceed recording for a while.
-                assertThat(latchForVideoRecording.await(VIDEO_TIMEOUT_SEC, TimeUnit.SECONDS))
-                    .isTrue()
-            } catch (ex: Exception) {
-                recording.stop()
-                throw ex
+                inOrder.verify(mockListener, timeout(5000L))
+                    .accept(any(VideoRecordEvent.Start::class.java))
+                inOrder.verify(mockListener, timeout(15000L).atLeast(5))
+                    .accept(any(VideoRecordEvent.Status::class.java))
+
+                activeRecording.stop()
             }
 
-            return recording
+        inOrder.verify(mockListener, timeout(5000L))
+            .accept(any(VideoRecordEvent.Finalize::class.java))
+
+        val captor = ArgumentCaptor.forClass(VideoRecordEvent::class.java)
+        verify(mockListener, atLeastOnce()).accept(captor.capture())
+
+        val finalizeEvent = captor.allValues.last() as VideoRecordEvent.Finalize
+
+        assertRecordingSuccessful(finalizeEvent, checkAudio = includeAudio)
+    }
+
+    private fun assertRecordingSuccessful(
+        finalizeEvent: VideoRecordEvent.Finalize,
+        checkAudio: Boolean = false
+    ) {
+        assertWithMessage(
+            "Recording did not finish successfully. Finished with error: ${
+                VideoRecordEvent.Finalize.errorToString(
+                    finalizeEvent.error
+                )
+            }"
+        ).that(finalizeEvent.error).isEqualTo(ERROR_NONE)
+        if (checkAudio) {
+            val audioStats = finalizeEvent.recordingStats.audioStats
+            assertWithMessage(
+                "Recording with audio encountered audio error." +
+                    "\n${audioStats.errorCause?.stackTraceToString()}"
+            ).that(audioStats.audioState).isNotEqualTo(AudioStats.AUDIO_STATE_ENCODER_ERROR)
         }
+    }
+
+    private fun startVideoRecording(videoCapture: VideoCapture<Recorder>, file: File):
+        Recording {
+        val recording = videoCapture.output
+            .prepareRecording(context, FileOutputOptions.Builder(file).build())
+            .start(CameraXExecutors.directExecutor(), videoRecordEventListener)
+
+        try {
+            // Wait for status event to proceed recording for a while.
+            assertThat(latchForVideoRecording.await(VIDEO_TIMEOUT_SEC, TimeUnit.SECONDS))
+                .isTrue()
+        } catch (ex: Exception) {
+            recording.stop()
+            throw ex
+        }
+
+        return recording
+    }
 
     private fun completeVideoRecording(videoCapture: VideoCapture<Recorder>, file: File) {
         val recording = startVideoRecording(videoCapture, file)
@@ -761,6 +812,7 @@
                 ) {
                     // No-op
                 }
+
                 override fun onSafeToRelease(surfaceTexture: SurfaceTexture) {
                     surfaceTexture.release()
                 }
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java b/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java
index 29cb814..da60534 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/Recorder.java
@@ -283,6 +283,8 @@
     private static final int AUDIO_CACHE_SIZE = 60;
     @VisibleForTesting
     static final EncoderFactory DEFAULT_ENCODER_FACTORY = EncoderImpl::new;
+    private static final Executor AUDIO_EXECUTOR =
+            CameraXExecutors.newSequentialExecutor(CameraXExecutors.ioExecutor());
 
     private final MutableStateObservable<StreamInfo> mStreamInfo;
     // Used only by getExecutor()
@@ -1261,7 +1263,11 @@
         AudioSource.Settings audioSourceSettings =
                 resolveAudioSourceSettings(audioMimeInfo, mediaSpec.getAudioSpec());
         try {
+            if (mAudioSource != null) {
+                releaseCurrentAudioSource();
+            }
             mAudioSource = setupAudioSource(recordingToStart, audioSourceSettings);
+            Logger.d(TAG, String.format("Set up new audio source: 0x%x", mAudioSource.hashCode()));
         } catch (AudioSourceAccessException e) {
             throw new ResourceCreationException(e);
         }
@@ -1290,35 +1296,34 @@
             throws AudioSourceAccessException {
 
         AudioSource audioSource = recordingToStart.performOneTimeAudioSourceCreation(
-                audioSourceSettings, CameraXExecutors.ioExecutor());
-
-        audioSource.setAudioSourceCallback(mSequentialExecutor,
-                new AudioSource.AudioSourceCallback() {
-                    @Override
-                    public void onSilenced(boolean silenced) {
-                        if (mIsAudioSourceSilenced != silenced) {
-                            mIsAudioSourceSilenced = silenced;
-                            mAudioErrorCause = silenced ? new IllegalStateException(
-                                    "The audio source has been silenced.") : null;
-                            updateInProgressStatusEvent();
-                        } else {
-                            Logger.w(TAG, "Audio source silenced transitions to the same state "
-                                    + silenced);
-                        }
-                    }
-
-                    @Override
-                    public void onError(@NonNull Throwable throwable) {
-                        if (throwable instanceof AudioSourceAccessException) {
-                            setAudioState(AudioState.DISABLED);
-                            updateInProgressStatusEvent();
-                        }
-                    }
-                });
+                audioSourceSettings, AUDIO_EXECUTOR);
 
         return audioSource;
     }
 
+    private void releaseCurrentAudioSource() {
+        if (mAudioSource == null) {
+            throw new AssertionError("Cannot release null audio source.");
+        }
+        AudioSource audioSource = mAudioSource;
+        mAudioSource = null;
+        Logger.d(TAG, String.format("Releasing audio source: 0x%x", audioSource.hashCode()));
+        // Run callback on direct executor since it is only logging
+        Futures.addCallback(audioSource.release(), new FutureCallback<Void>() {
+            @Override
+            public void onSuccess(@Nullable Void result) {
+                Logger.d(TAG, String.format("Released audio source successfully: 0x%x",
+                        audioSource.hashCode()));
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                Logger.d(TAG, String.format("An error occurred while attempting to "
+                        + "release audio source: 0x%x", audioSource.hashCode()));
+            }
+        }, CameraXExecutors.directExecutor());
+    }
+
     @ExecutedBy("mSequentialExecutor")
     private void setupVideo(@NonNull SurfaceRequest surfaceRequest) {
         MediaSpec mediaSpec = getObservableData(mMediaSpec);
@@ -1611,7 +1616,7 @@
                 break;
         }
 
-        initEncoderCallbacks(recordingToStart);
+        initEncoderAndAudioSourceCallbacks(recordingToStart);
         if (isAudioEnabled()) {
             mAudioSource.start();
             mAudioEncoder.start();
@@ -1624,7 +1629,7 @@
     }
 
     @ExecutedBy("mSequentialExecutor")
-    private void initEncoderCallbacks(@NonNull RecordingRecord recordingToStart) {
+    private void initEncoderAndAudioSourceCallbacks(@NonNull RecordingRecord recordingToStart) {
         mEncodingFutures.add(CallbackToFutureAdapter.getFuture(
                 completer -> {
                     mVideoEncoder.setEncoderCallback(new EncoderCallback() {
@@ -1723,6 +1728,43 @@
         if (isAudioEnabled()) {
             mEncodingFutures.add(CallbackToFutureAdapter.getFuture(
                     completer -> {
+                        Consumer<Throwable> audioErrorConsumer = throwable -> {
+                            if (mAudioErrorCause == null) {
+                                // If the audio source or encoder encounters error, update the
+                                // status event to notify users. Then continue recording without
+                                // audio data.
+                                setAudioState(AudioState.ERROR);
+                                mAudioErrorCause = throwable;
+                                updateInProgressStatusEvent();
+                                completer.set(null);
+                            }
+                        };
+
+                        mAudioSource.setAudioSourceCallback(mSequentialExecutor,
+                                new AudioSource.AudioSourceCallback() {
+                                    @Override
+                                    public void onSilenced(boolean silenced) {
+                                        if (mIsAudioSourceSilenced != silenced) {
+                                            mIsAudioSourceSilenced = silenced;
+                                            mAudioErrorCause = silenced ? new IllegalStateException(
+                                                    "The audio source has been silenced.") : null;
+                                            updateInProgressStatusEvent();
+                                        } else {
+                                            Logger.w(TAG, "Audio source silenced transitions"
+                                                    + " to the same state " + silenced);
+                                        }
+                                    }
+
+                                    @Override
+                                    public void onError(@NonNull Throwable throwable) {
+                                        Logger.e(TAG, "Error occurred after audio source started.",
+                                                throwable);
+                                        if (throwable instanceof AudioSourceAccessException) {
+                                            audioErrorConsumer.accept(throwable);
+                                        }
+                                    }
+                                });
+
                         mAudioEncoder.setEncoderCallback(new EncoderCallback() {
                             @ExecutedBy("mSequentialExecutor")
                             @Override
@@ -1739,12 +1781,9 @@
                             @ExecutedBy("mSequentialExecutor")
                             @Override
                             public void onEncodeError(@NonNull EncodeException e) {
-                                // If the audio encoder encounters error, update the status event
-                                // to notify users. Then continue recording without audio data.
-                                setAudioState(AudioState.ERROR);
-                                mAudioErrorCause = e;
-                                updateInProgressStatusEvent();
-                                completer.set(null);
+                                if (mAudioErrorCause == null) {
+                                    audioErrorConsumer.accept(e);
+                                }
                             }
 
                             @ExecutedBy("mSequentialExecutor")
@@ -2005,9 +2044,7 @@
             mVideoOutputConfig = null;
         }
         if (mAudioSource != null) {
-            Logger.d(TAG, "Releasing audio source.");
-            mAudioSource.release();
-            mAudioSource = null;
+            releaseCurrentAudioSource();
         }
 
         setAudioState(AudioState.INITIALIZING);
@@ -2122,6 +2159,7 @@
                 // Fall-through
             case ACTIVE:
                 setAudioState(AudioState.IDLING);
+                mAudioSource.stop();
                 break;
             case ERROR:
                 // Reset audio state to INITIALIZING if the audio encoder encountered error, so
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSource.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSource.java
index 987aa380..f7129d4 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSource.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSource.java
@@ -46,9 +46,11 @@
 import androidx.camera.video.internal.compat.Api29Impl;
 import androidx.camera.video.internal.compat.Api31Impl;
 import androidx.camera.video.internal.encoder.InputBuffer;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
 import androidx.core.util.Preconditions;
 
 import com.google.auto.value.AutoValue;
+import com.google.common.util.concurrent.ListenableFuture;
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
@@ -252,7 +254,7 @@
                     }
                     break;
                 case RELEASED:
-                    throw new IllegalStateException("AudioRecorder is released");
+                    throw new AssertionError("AudioRecorder is released");
             }
         });
     }
@@ -266,8 +268,6 @@
      *
      * <p>Audio data will start being sent to the {@link BufferProvider} when
      * {@link BufferProvider}'s state is {@link BufferProvider.State#ACTIVE}.
-     *
-     * @throws IllegalStateException if the AudioSource is released.
      */
     public void start() {
         mExecutor.execute(() -> {
@@ -280,7 +280,7 @@
                     // Do nothing
                     break;
                 case RELEASED:
-                    throw new IllegalStateException("AudioRecorder is released");
+                    throw new AssertionError("AudioRecorder is released");
             }
         });
     }
@@ -289,8 +289,6 @@
      * Stops the AudioSource.
      *
      * <p>Audio data will stop being sent to the {@link BufferProvider}.
-     *
-     * @throws IllegalStateException if it is released.
      */
     public void stop() {
         mExecutor.execute(() -> {
@@ -303,7 +301,8 @@
                     // Do nothing
                     break;
                 case RELEASED:
-                    throw new IllegalStateException("AudioRecorder is released");
+                    Logger.w(TAG, "AudioRecorder is released. "
+                            + "Calling stop() is a no-op.");
             }
         });
     }
@@ -313,25 +312,35 @@
      *
      * <p>Once the AudioSource is released, it can not be used any more.
      */
-    public void release() {
-        mExecutor.execute(() -> {
-            switch (mState) {
-                case STARTED:
-                    // Fall-through
-                case CONFIGURED:
-                    resetBufferProvider(null);
-                    if (Build.VERSION.SDK_INT >= 29) {
-                        Api29Impl.unregisterAudioRecordingCallback(mAudioRecord,
-                                mAudioRecordingCallback);
+    @NonNull
+    public ListenableFuture<Void> release() {
+        return CallbackToFutureAdapter.getFuture(completer -> {
+            mExecutor.execute(() -> {
+                try {
+                    switch (mState) {
+                        case STARTED:
+                            // Fall-through
+                        case CONFIGURED:
+                            resetBufferProvider(null);
+                            if (Build.VERSION.SDK_INT >= 29) {
+                                Api29Impl.unregisterAudioRecordingCallback(mAudioRecord,
+                                        mAudioRecordingCallback);
+                            }
+                            mAudioRecord.release();
+                            stopSendingAudio();
+                            setState(RELEASED);
+                            break;
+                        case RELEASED:
+                            // Do nothing
+                            break;
                     }
-                    mAudioRecord.release();
-                    stopSendingAudio();
-                    setState(RELEASED);
-                    break;
-                case RELEASED:
-                    // Do nothing
-                    break;
-            }
+                    completer.set(null);
+                } catch (Throwable t) {
+                    completer.setException(t);
+                }
+            });
+
+            return "AudioSource-release";
         });
     }
 
@@ -354,7 +363,7 @@
                 case STARTED:
                     // Fall-through
                 case RELEASED:
-                    throw new IllegalStateException("The audio recording callback must be "
+                    throw new AssertionError("The audio recording callback must be "
                             + "registered before the audio source is started.");
             }
         });
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
index a2fceec..b21f7bf 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/CameraController.java
@@ -847,7 +847,7 @@
      * <p>Setting an analyzer function replaces any previous analyzer. Only one analyzer can be
      * set at any time.
      *
-     * <p> If the {@link ImageAnalysis.Analyzer#getTargetResolutionOverride()} returns a non-null
+     * <p> If the {@link ImageAnalysis.Analyzer#getDefaultTargetResolution()} returns a non-null
      * value, calling this method will reconfigure the camera which might cause additional
      * latency. To avoid this, set the value before controller is bound to the lifecycle.
      *
@@ -875,7 +875,7 @@
      *
      * <p>This will stop data from streaming to the {@link ImageAnalysis}.
      *
-     * <p> If the current {@link ImageAnalysis.Analyzer#getTargetResolutionOverride()} returns
+     * <p> If the current {@link ImageAnalysis.Analyzer#getDefaultTargetResolution()} returns
      * non-null value, calling this method will reconfigure the camera which might cause additional
      * latency. To avoid this, call this method when the lifecycle is not active.
      *
@@ -895,9 +895,9 @@
             @Nullable ImageAnalysis.Analyzer oldAnalyzer,
             @Nullable ImageAnalysis.Analyzer newAnalyzer) {
         Size oldResolution = oldAnalyzer == null ? null :
-                oldAnalyzer.getTargetResolutionOverride();
+                oldAnalyzer.getDefaultTargetResolution();
         Size newResolution = newAnalyzer == null ? null :
-                newAnalyzer.getTargetResolutionOverride();
+                newAnalyzer.getDefaultTargetResolution();
         if (!Objects.equals(oldResolution, newResolution)) {
             // Rebind ImageAnalysis to reconfigure target resolution.
             unbindImageAnalysisAndRecreate(mImageAnalysis.getBackpressureStrategy(),
@@ -1089,7 +1089,6 @@
         }
         if (outputTransform == null) {
             mAnalysisAnalyzer.updateTransform(null);
-
         } else if (mAnalysisAnalyzer.getTargetCoordinateSystem()
                 == COORDINATE_SYSTEM_VIEW_REFERENCED) {
             mAnalysisAnalyzer.updateTransform(outputTransform.getMatrix());
diff --git a/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt b/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
index fd9935f..d69c153 100644
--- a/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
+++ b/camera/camera-view/src/test/java/androidx/camera/view/CameraControllerTest.kt
@@ -127,7 +127,7 @@
                 // no-op
             }
 
-            override fun getTargetResolutionOverride(): Size? {
+            override fun getDefaultTargetResolution(): Size? {
                 return size
             }
         }
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureLatencyTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureLatencyTest.kt
new file mode 100644
index 0000000..552b6c28
--- /dev/null
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureLatencyTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2022 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.integration.core
+
+import android.content.Context
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.camera2.pipe.integration.CameraPipeConfig
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.CameraXConfig
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.ImageProxy
+import androidx.camera.core.Logger
+import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.LabTestRule
+import androidx.camera.testing.fakes.FakeLifecycleOwner
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.LargeTest
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import org.junit.After
+import org.junit.Assert.assertTrue
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+private const val NUM_IMAGES = 10
+
+/**
+ * Profiles the ImageCapture performance for capturing images in different capture modes.
+ *
+ * Time measurement is taken from the time the capture request sent to when an ImageProxy is
+ * returned. Saving an image to disk is not counted.
+ *
+ * Measurement is the total time for capturing NUM_IMAGES images.
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+class ImageCaptureLatencyTest(
+    private val implName: String,
+    private val cameraXConfig: CameraXConfig
+) {
+
+    @get:Rule
+    val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
+        CameraUtil.PreTestCameraIdList(cameraXConfig)
+    )
+
+    @get:Rule
+    val labTest = LabTestRule()
+
+    private val context = ApplicationProvider.getApplicationContext<Context>()
+    private lateinit var camera: CameraUseCaseAdapter
+    private lateinit var cameraProvider: ProcessCameraProvider
+    private lateinit var fakeLifecycleOwner: FakeLifecycleOwner
+
+    companion object {
+        private const val TAG = "ImageCaptureLatencyTest"
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun data() = listOf(
+            arrayOf(Camera2Config::class.simpleName, Camera2Config.defaultConfig()),
+            arrayOf(CameraPipeConfig::class.simpleName, CameraPipeConfig.defaultConfig())
+        )
+    }
+
+    @Before
+    fun setUp() = runBlocking {
+        Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK))
+        ProcessCameraProvider.configureInstance(cameraXConfig)
+        cameraProvider = ProcessCameraProvider.getInstance(context).get(10, TimeUnit.SECONDS)
+
+        withContext(Dispatchers.Main) {
+            fakeLifecycleOwner = FakeLifecycleOwner()
+            fakeLifecycleOwner.startAndResume()
+        }
+    }
+
+    @After
+    fun tearDown() = runBlocking {
+        if (::cameraProvider.isInitialized) {
+            withContext(Dispatchers.Main) {
+                cameraProvider.unbindAll()
+                cameraProvider.shutdown()
+            }
+        }
+    }
+
+    @Test
+    fun imageCaptureMeasurementZsl() {
+        imageCaptureMeasurement(ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG)
+    }
+
+    @Test
+    fun imageCaptureMeasurementMinimizeLatency() {
+        imageCaptureMeasurement(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
+    }
+
+    @Test
+    fun imageCaptureMeasurementMaximizeQuality() {
+        imageCaptureMeasurement(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
+    }
+
+    private fun imageCaptureMeasurement(captureMode: Int) {
+
+        val imageCapture = ImageCapture.Builder().setCaptureMode(captureMode).build()
+
+        camera = CameraUtil.createCameraAndAttachUseCase(
+            context,
+            CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build(),
+            imageCapture
+        )
+
+        // Skip if capture mode is ZSL and the device doesn't support ZSL
+        if ((captureMode == ImageCapture.CAPTURE_MODE_ZERO_SHUTTER_LAG) &&
+            !camera.cameraInfo.isZslSupported) {
+            Logger.d(TAG, "Skipping due to no ZSL support")
+            return
+        }
+
+        val startTimeMillis = System.currentTimeMillis()
+
+        val countDownLatch = CountDownLatch(NUM_IMAGES)
+
+        for (i in 1..NUM_IMAGES) {
+            imageCapture.takePicture(
+                Dispatchers.Main.asExecutor(),
+                object : ImageCapture.OnImageCapturedCallback() {
+                    override fun onCaptureSuccess(image: ImageProxy) {
+                        image.close()
+                        countDownLatch.countDown()
+                    }
+                })
+        }
+
+        assertTrue(countDownLatch.await(60, TimeUnit.SECONDS))
+
+        val duration = System.currentTimeMillis() - startTimeMillis
+
+        // This log is used to profile the ImageCapture performance. The log parser identifies the log
+        // pattern "Image capture performance profiling" in the device output log.
+        Logger.d(TAG,
+            "Image capture performance profiling, duration: [$duration] capture mode: $captureMode")
+    }
+}
\ No newline at end of file
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
index dc9e6ed..afabb4a 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
@@ -974,6 +974,12 @@
         // Clear listening frame update before unbind all.
         mPreviewRenderer.clearFrameUpdateListener();
 
+        // Remove ZoomState observer from old CameraInfo to prevent from receiving event from old
+        // CameraInfo
+        if (mCamera != null) {
+            mCamera.getCameraInfo().getZoomState().removeObservers(this);
+        }
+
         // Stop video recording if exists.
         if (mRecordUi.getState() == RecordUi.State.RECORDING
                 || mRecordUi.getState() == RecordUi.State.PAUSED) {
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
index 6ca67a8..1c82541 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Animation.kt
@@ -233,12 +233,19 @@
     override val isInfinite: Boolean get() = animationSpec.isInfinite
     override fun getValueFromNanos(playTimeNanos: Long): T {
         return if (!isFinishedFromNanos(playTimeNanos)) {
-            typeConverter.convertFromVector(
-                animationSpec.getValueFromNanos(
-                    playTimeNanos, initialValueVector,
-                    targetValueVector, initialVelocityVector
-                )
-            )
+            animationSpec.getValueFromNanos(
+                playTimeNanos, initialValueVector,
+                targetValueVector, initialVelocityVector
+            ).let {
+                // TODO: Remove after b/232030217
+                for (i in 0 until it.size) {
+                    check(!it.get(i).isNaN()) {
+                        "AnimationVector cannot contain a NaN. $it. Animation: $this," +
+                            " playTimeNanos: $playTimeNanos"
+                    }
+                }
+                typeConverter.convertFromVector(it)
+            }
         } else {
             targetValue
         }
@@ -272,7 +279,8 @@
 
     override fun toString(): String {
         return "TargetBasedAnimation: $initialValue -> $targetValue," +
-            "initial velocity: $initialVelocityVector, duration: $durationMillis ms"
+            "initial velocity: $initialVelocityVector, duration: $durationMillis ms," +
+            "animationSpec: $animationSpec"
     }
 }
 
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
index 6a00dfa..8276072 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
@@ -507,10 +507,15 @@
 
         internal fun onPlayTimeChanged(playTimeNanos: Long, durationScale: Float) {
             val playTime =
-                if (durationScale == 0f) {
-                    animation.durationNanos
+                if (durationScale > 0f) {
+                    val scaledTime = (playTimeNanos - offsetTimeNanos) / durationScale
+                    check(!scaledTime.isNaN()) {
+                        "Duration scale adjusted time is NaN. Duration scale: $durationScale," +
+                            "playTimeNanos: $playTimeNanos, offsetTimeNanos: $offsetTimeNanos"
+                    }
+                    scaledTime.toLong()
                 } else {
-                    ((playTimeNanos - offsetTimeNanos) / durationScale).toLong()
+                    animation.durationNanos
                 }
             value = animation.getValueFromNanos(playTime)
             velocityVector = animation.getVelocityVectorFromNanos(playTime)
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/OverscrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/OverscrollTest.kt
index 046d991..1c96893 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/OverscrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/OverscrollTest.kt
@@ -33,7 +33,9 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ImageBitmap
 import androidx.compose.ui.graphics.toPixelMap
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.platform.ViewConfiguration
@@ -448,6 +450,144 @@
         }
     }
 
+    @Test
+    fun horizontalOverscrollEnabled_verifyOverscrollReceivedSingleAxisValues() {
+        val controller = TestOverscrollEffect()
+        val scrollableState = ScrollableState { 0f }
+        rule.setOverscrollContentAndReturnViewConfig(
+            scrollableState = scrollableState,
+            overscrollEffect = controller
+        )
+
+        rule.runOnIdle {
+            // we passed isContentScrolls = 1, so initial draw must occur
+            assertThat(controller.drawCallsCount).isEqualTo(1)
+        }
+
+        rule.onNodeWithTag(boxTag).assertExists()
+
+        rule.onNodeWithTag(boxTag).performTouchInput {
+            swipeWithVelocity(
+                center,
+                Offset(centerX + 10800, centerY),
+                endVelocity = 30000f
+            )
+        }
+
+        rule.runOnIdle {
+            with(controller) {
+                // presented on consume pre scroll
+                assertSingleAxisValue(preScrollDelta.x, preScrollDelta.y)
+
+                // presented on consume post scroll
+                assertSingleAxisValue(lastOverscrollDelta.x, lastOverscrollDelta.y)
+                assertSingleAxisValue(lastInitialDragDelta.x, lastInitialDragDelta.y)
+
+                // presented on pre fling
+                assertSingleAxisValue(preFlingVelocity.x, preFlingVelocity.y)
+
+                // presented on post fling
+                assertSingleAxisValue(lastVelocity.x, lastVelocity.y)
+            }
+        }
+    }
+
+    @Test
+    fun verticalOverscrollEnabled_verifyOverscrollReceivedSingleAxisValues() {
+        val controller = TestOverscrollEffect()
+        val scrollableState = ScrollableState { 0f }
+        rule.setOverscrollContentAndReturnViewConfig(
+            scrollableState = scrollableState,
+            overscrollEffect = controller,
+            orientation = Orientation.Vertical
+        )
+
+        rule.runOnIdle {
+            // we passed isContentScrolls = 1, so initial draw must occur
+            assertThat(controller.drawCallsCount).isEqualTo(1)
+        }
+
+        rule.onNodeWithTag(boxTag).assertExists()
+
+        rule.onNodeWithTag(boxTag).performTouchInput {
+            swipeWithVelocity(
+                center,
+                Offset(centerX, centerY + 10800),
+                endVelocity = 30000f
+            )
+        }
+
+        rule.runOnIdle {
+            with(controller) {
+                // presented on consume pre scroll
+                assertSingleAxisValue(preScrollDelta.y, preScrollDelta.x)
+
+                // presented on consume post scroll
+                assertSingleAxisValue(lastOverscrollDelta.y, lastOverscrollDelta.x)
+                assertSingleAxisValue(lastInitialDragDelta.y, lastInitialDragDelta.x)
+
+                // presented on pre fling
+                assertSingleAxisValue(preFlingVelocity.y, preFlingVelocity.x)
+
+                // presented on post fling
+                assertSingleAxisValue(lastVelocity.y, lastVelocity.x)
+            }
+        }
+    }
+
+    @Test
+    fun verticalOverscrollEnabled_notTriggered_verifyCrossAxisIsCorrectlyPropagated() {
+        val controller = TestOverscrollEffect()
+        val inspectableConnection = InspectableConnection()
+        rule.setOverscrollContentAndReturnViewConfig(
+            scrollableState = ScrollableState { 0f },
+            overscrollEffect = controller,
+            orientation = Orientation.Vertical,
+            inspectableConnection = inspectableConnection
+        )
+
+        rule.onNodeWithTag(boxTag).assertExists()
+
+        rule.onNodeWithTag(boxTag).performTouchInput {
+            down(center)
+            moveBy(Offset(100f, 100f))
+            up()
+        }
+
+        rule.runOnIdle {
+            assertThat(inspectableConnection.preScrollOffset.x).isEqualTo(100f)
+        }
+    }
+
+    @Test
+    fun horizontalOverscrollEnabled_notTriggered_verifyCrossAxisIsCorrectlyPropagated() {
+        val controller = TestOverscrollEffect()
+        val inspectableConnection = InspectableConnection()
+        rule.setOverscrollContentAndReturnViewConfig(
+            scrollableState = ScrollableState { 0f },
+            overscrollEffect = controller,
+            orientation = Orientation.Horizontal,
+            inspectableConnection = inspectableConnection
+        )
+
+        rule.onNodeWithTag(boxTag).assertExists()
+
+        rule.onNodeWithTag(boxTag).performTouchInput {
+            down(center)
+            moveBy(Offset(100f, 100f))
+            up()
+        }
+
+        rule.runOnIdle {
+            assertThat(inspectableConnection.preScrollOffset.y).isEqualTo(100f)
+        }
+    }
+
+    private fun assertSingleAxisValue(mainAxis: Float, crossAxis: Float) {
+        assertThat(abs(mainAxis)).isGreaterThan(0)
+        assertThat(crossAxis).isEqualTo(0)
+    }
+
     class TestOverscrollEffect(
         private val consumePreCycles: Boolean = false,
         var animationRunning: Boolean = false
@@ -592,12 +732,14 @@
     scrollableState: ScrollableState,
     overscrollEffect: OverscrollEffect,
     flingBehavior: FlingBehavior? = null,
-    reverseDirection: Boolean = false
+    reverseDirection: Boolean = false,
+    orientation: Orientation = Orientation.Horizontal,
+    inspectableConnection: NestedScrollConnection = NoOpConnection
 ): ViewConfiguration {
     var viewConfiguration: ViewConfiguration? = null
     setContent {
         viewConfiguration = LocalViewConfiguration.current
-        Box {
+        Box(Modifier.nestedScroll(inspectableConnection)) {
             Box(
                 Modifier
                     .testTag("box")
@@ -605,7 +747,7 @@
                     .overscroll(overscrollEffect)
                     .scrollable(
                         state = scrollableState,
-                        orientation = Orientation.Horizontal,
+                        orientation = orientation,
                         overscrollEffect = overscrollEffect,
                         flingBehavior = flingBehavior ?: ScrollableDefaults.flingBehavior(),
                         reverseDirection = reverseDirection
@@ -626,3 +768,14 @@
         }
     }
 }
+
+private val NoOpConnection = object : NestedScrollConnection {}
+
+private class InspectableConnection : NestedScrollConnection {
+    var preScrollOffset = Offset.Zero
+
+    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+        preScrollOffset += available
+        return Offset.Zero
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
index 44589d7..49053f2 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollableTest.kt
@@ -24,13 +24,20 @@
 import androidx.compose.foundation.gestures.ScrollScope
 import androidx.compose.foundation.gestures.ScrollableState
 import androidx.compose.foundation.gestures.animateScrollBy
+import androidx.compose.foundation.gestures.awaitFirstDown
 import androidx.compose.foundation.gestures.scrollable
 import androidx.compose.foundation.interaction.DragInteraction
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.currentComposer
@@ -39,6 +46,7 @@
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusDirection
 import androidx.compose.ui.focus.FocusManager
@@ -46,13 +54,19 @@
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollDispatcher
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.input.pointer.util.VelocityTracker
 import androidx.compose.ui.materialize
 import androidx.compose.ui.modifier.ModifierLocalConsumer
 import androidx.compose.ui.modifier.ModifierLocalReadScope
+import androidx.compose.ui.platform.AbstractComposeView
 import androidx.compose.ui.platform.InspectableValue
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
@@ -65,22 +79,37 @@
 import androidx.compose.ui.test.performMouseInput
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.test.swipe
+import androidx.compose.ui.test.swipeDown
+import androidx.compose.ui.test.swipeLeft
+import androidx.compose.ui.test.swipeRight
+import androidx.compose.ui.test.swipeUp
 import androidx.compose.ui.test.swipeWithVelocity
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.CoordinatesProvider
+import androidx.test.espresso.action.GeneralLocation
+import androidx.test.espresso.action.GeneralSwipeAction
+import androidx.test.espresso.action.Press
+import androidx.test.espresso.action.Swipe
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import kotlin.math.abs
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import org.hamcrest.CoreMatchers.allOf
+import org.hamcrest.CoreMatchers.instanceOf
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import kotlin.math.abs
 
 @LargeTest
 @RunWith(AndroidJUnit4::class)
@@ -2040,6 +2069,161 @@
         rule.runOnIdle { assertThat(nextItemIsFocused).isTrue() }
     }
 
+    @Test
+    fun verticalScrollable_assertVelocityCalculationIsSimilarInsideOutsideVelocityTracker() {
+        // arrange
+        val tracker = VelocityTracker()
+        var velocity = Velocity.Zero
+        val capturingScrollConnection = object : NestedScrollConnection {
+            override suspend fun onPreFling(available: Velocity): Velocity {
+                velocity += available
+                return Velocity.Zero
+            }
+        }
+        val controller = ScrollableState { _ -> 0f }
+
+        setScrollableContent {
+            Modifier
+                .pointerInput(Unit) {
+                    savePointerInputEvents(tracker, this)
+                }
+                .nestedScroll(capturingScrollConnection)
+                .scrollable(controller, Orientation.Vertical)
+        }
+
+        // act
+        rule.onNodeWithTag(scrollableBoxTag).performTouchInput {
+            swipeUp()
+        }
+
+        // assert
+        rule.runOnIdle {
+            val diff = abs((velocity - tracker.calculateVelocity()).y)
+            assertThat(diff).isLessThan(VelocityTrackerCalculationThreshold)
+        }
+        tracker.resetTracking()
+        velocity = Velocity.Zero
+
+        // act
+        rule.onNodeWithTag(scrollableBoxTag).performTouchInput {
+            swipeDown()
+        }
+
+        // assert
+        rule.runOnIdle {
+            val diff = abs((velocity - tracker.calculateVelocity()).y)
+            assertThat(diff).isLessThan(VelocityTrackerCalculationThreshold)
+        }
+    }
+
+    @Test
+    fun horizontalScrollable_assertVelocityCalculationIsSimilarInsideOutsideVelocityTracker() {
+        // arrange
+        val tracker = VelocityTracker()
+        var velocity = Velocity.Zero
+        val capturingScrollConnection = object : NestedScrollConnection {
+            override suspend fun onPreFling(available: Velocity): Velocity {
+                velocity += available
+                return Velocity.Zero
+            }
+        }
+        val controller = ScrollableState { _ -> 0f }
+
+        setScrollableContent {
+            Modifier
+                .pointerInput(Unit) {
+                    savePointerInputEvents(tracker, this)
+                }
+                .nestedScroll(capturingScrollConnection)
+                .scrollable(controller, Orientation.Horizontal)
+        }
+
+        // act
+        rule.onNodeWithTag(scrollableBoxTag).performTouchInput {
+            swipeLeft()
+        }
+
+        // assert
+        rule.runOnIdle {
+            val diff = abs((velocity - tracker.calculateVelocity()).x)
+            assertThat(diff).isLessThan(VelocityTrackerCalculationThreshold)
+        }
+        tracker.resetTracking()
+        velocity = Velocity.Zero
+
+        // act
+        rule.onNodeWithTag(scrollableBoxTag).performTouchInput {
+            swipeRight()
+        }
+
+        // assert
+        rule.runOnIdle {
+            val diff = abs((velocity - tracker.calculateVelocity()).x)
+            assertThat(diff).isLessThan(VelocityTrackerCalculationThreshold)
+        }
+    }
+
+    @Test
+    fun offsetsScrollable_velocityCalculationShouldConsiderLocalPositions() {
+        // arrange
+        var velocity = Velocity.Zero
+        val fullScreen = mutableStateOf(false)
+        lateinit var scrollState: LazyListState
+        val capturingScrollConnection = object : NestedScrollConnection {
+            override suspend fun onPreFling(available: Velocity): Velocity {
+                velocity += available
+                return Velocity.Zero
+            }
+        }
+        rule.setContent {
+            scrollState = rememberLazyListState()
+            Column(modifier = Modifier.nestedScroll(capturingScrollConnection)) {
+                if (!fullScreen.value) {
+                    Box(
+                        modifier = Modifier
+                            .fillMaxWidth()
+                            .background(Color.Black)
+                            .height(400.dp)
+                    )
+                }
+
+                LazyColumn(state = scrollState) {
+                    items(100) {
+                        Box(
+                            modifier = Modifier
+                                .padding(10.dp)
+                                .background(Color.Red)
+                                .fillMaxWidth()
+                                .height(50.dp)
+                        )
+                    }
+                }
+            }
+        }
+        // act
+        // Register generated velocity with offset
+        composeViewSwipeUp()
+        rule.waitForIdle()
+        val previousVelocity = velocity
+        velocity = Velocity.Zero
+        // Remove offset and restart scroll
+        fullScreen.value = true
+        rule.runOnIdle {
+            runBlocking {
+                scrollState.scrollToItem(0)
+            }
+        }
+        rule.waitForIdle()
+        // Register generated velocity without offset, should be larger as there was more
+        // screen to cover.
+        composeViewSwipeUp()
+
+        // assert
+        rule.runOnIdle {
+            assertThat(abs(previousVelocity.y)).isNotEqualTo(abs(velocity.y))
+        }
+    }
+
     private fun setScrollableContent(scrollableModifierFactory: @Composable () -> Modifier) {
         rule.setContentAndGetScope {
             Box {
@@ -2083,3 +2267,88 @@
 
     override val effectModifier: Modifier get() = Modifier
 }
+
+// Very low tolerance on the difference
+internal val VelocityTrackerCalculationThreshold = 1
+
+@OptIn(ExperimentalComposeUiApi::class)
+internal suspend fun savePointerInputEvents(
+    tracker: VelocityTracker,
+    pointerInputScope: PointerInputScope
+) {
+    with(pointerInputScope) {
+        coroutineScope {
+            awaitPointerEventScope {
+                while (true) {
+                    var event = awaitFirstDown()
+                    tracker.addPosition(event.uptimeMillis, event.position)
+                    while (!event.changedToUpIgnoreConsumed()) {
+                        val currentEvent = awaitPointerEvent().changes
+                            .firstOrNull()
+
+                        if (currentEvent != null && !currentEvent.changedToUpIgnoreConsumed()) {
+                            currentEvent.historical.fastForEach {
+                                tracker.addPosition(it.uptimeMillis, it.position)
+                            }
+                            tracker.addPosition(
+                                currentEvent.uptimeMillis,
+                                currentEvent.position
+                            )
+                            event = currentEvent
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+internal fun composeViewSwipeUp() {
+    onView(allOf(instanceOf(AbstractComposeView::class.java)))
+        .perform(
+            espressoSwipe(
+                GeneralLocation.BOTTOM_CENTER,
+                GeneralLocation.CENTER
+            )
+        )
+}
+
+internal fun composeViewSwipeDown() {
+    onView(allOf(instanceOf(AbstractComposeView::class.java)))
+        .perform(
+            espressoSwipe(
+                GeneralLocation.CENTER,
+                GeneralLocation.BOTTOM_CENTER
+            )
+        )
+}
+
+internal fun composeViewSwipeLeft() {
+    onView(allOf(instanceOf(AbstractComposeView::class.java)))
+        .perform(
+            espressoSwipe(
+                GeneralLocation.CENTER,
+                GeneralLocation.CENTER_LEFT
+            )
+        )
+}
+
+internal fun composeViewSwipeRight() {
+    onView(allOf(instanceOf(AbstractComposeView::class.java)))
+        .perform(
+            espressoSwipe(
+                GeneralLocation.CENTER,
+                GeneralLocation.CENTER_RIGHT
+            )
+        )
+}
+
+private fun espressoSwipe(
+    start: CoordinatesProvider,
+    end: CoordinatesProvider
+): GeneralSwipeAction {
+    return GeneralSwipeAction(
+        Swipe.FAST, start, end,
+        Press.FINGER
+    )
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/BaseLazyListTestWithOrientation.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/BaseLazyListTestWithOrientation.kt
index 1c3c0ae..1919263 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/BaseLazyListTestWithOrientation.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/BaseLazyListTestWithOrientation.kt
@@ -18,6 +18,10 @@
 
 import androidx.compose.animation.core.snap
 import androidx.compose.foundation.AutoTestFrameClock
+import androidx.compose.foundation.composeViewSwipeDown
+import androidx.compose.foundation.composeViewSwipeLeft
+import androidx.compose.foundation.composeViewSwipeRight
+import androidx.compose.foundation.composeViewSwipeUp
 import androidx.compose.foundation.gestures.FlingBehavior
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.ScrollableDefaults
@@ -180,6 +184,22 @@
         density = rule.density
     )
 
+    fun composeViewSwipeForward() {
+        if (orientation == Orientation.Vertical) {
+            composeViewSwipeUp()
+        } else {
+            composeViewSwipeLeft()
+        }
+    }
+
+    fun composeViewSwipeBackward() {
+        if (orientation == Orientation.Vertical) {
+            composeViewSwipeDown()
+        } else {
+            composeViewSwipeRight()
+        }
+    }
+
     @Composable
     fun LazyColumnOrRow(
         modifier: Modifier = Modifier,
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListTest.kt
index 48a7d4b..b0f7d04 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListTest.kt
@@ -18,6 +18,7 @@
 
 import android.os.Build
 import androidx.compose.foundation.AutoTestFrameClock
+import androidx.compose.foundation.VelocityTrackerCalculationThreshold
 import androidx.compose.foundation.background
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Box
@@ -26,6 +27,7 @@
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.requiredHeight
 import androidx.compose.foundation.layout.requiredSize
@@ -36,6 +38,7 @@
 import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.lazy.itemsIndexed
 import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.savePointerInputEvents
 import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
@@ -57,6 +60,8 @@
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
 import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.input.pointer.util.VelocityTracker
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.SemanticsActions
@@ -83,6 +88,7 @@
 import androidx.compose.ui.test.swipeWithVelocity
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.zIndex
 import androidx.test.filters.LargeTest
@@ -92,6 +98,7 @@
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import java.util.concurrent.CountDownLatch
+import kotlin.math.abs
 import kotlinx.coroutines.runBlocking
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -1923,6 +1930,58 @@
             .assertStartPositionInRootIsEqualTo(0.dp)
     }
 
+    @Test
+    fun assertVelocityCalculationIsSimilar_witHistoricalValues() {
+        // arrange
+        val tracker = VelocityTracker()
+        var velocity = Velocity.Zero
+        val capturingScrollConnection = object : NestedScrollConnection {
+            override suspend fun onPreFling(available: Velocity): Velocity {
+                velocity += available
+                return Velocity.Zero
+            }
+        }
+        rule.setContent {
+            Box(modifier = Modifier
+                .background(Color.Yellow)
+                .nestedScroll(capturingScrollConnection)
+                .fillMaxWidth()
+                .pointerInput(Unit) {
+                    savePointerInputEvents(tracker, this)
+                }) {
+                LazyColumnOrRow {
+                    items(200) {
+                        Box(modifier = Modifier
+                            .fillMaxWidth()
+                            .height(48.dp)
+                            .padding(8.dp)
+                            .background(Color.Blue))
+                    }
+                }
+            }
+        }
+
+        // act
+        composeViewSwipeForward()
+
+        // assert
+        rule.runOnIdle {
+            val diff = abs((velocity - tracker.calculateVelocity()).y)
+            assertThat(diff).isLessThan(VelocityTrackerCalculationThreshold)
+        }
+        tracker.resetTracking()
+        velocity = Velocity.Zero
+
+        // act
+        composeViewSwipeBackward()
+
+        // assert
+        rule.runOnIdle {
+            val diff = abs((velocity - tracker.calculateVelocity()).y)
+            assertThat(diff).isLessThan(VelocityTrackerCalculationThreshold)
+        }
+    }
+
     // ********************* END OF TESTS *********************
     // Helper functions, etc. live below here
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
index 1a75b6c..1f0519e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Scrollable.kt
@@ -310,12 +310,18 @@
         else -> Offset(0f, this)
     }
 
+    fun Offset.singleAxisOffset(): Offset =
+        if (orientation == Horizontal) copy(y = 0f) else copy(x = 0f)
+
     fun Offset.toFloat(): Float =
         if (orientation == Horizontal) this.x else this.y
 
     fun Velocity.toFloat(): Float =
         if (orientation == Horizontal) this.x else this.y
 
+    fun Velocity.singleAxisVelocity(): Velocity =
+        if (orientation == Horizontal) copy(y = 0f) else copy(x = 0f)
+
     fun Velocity.update(newValue: Float): Velocity =
         if (orientation == Horizontal) copy(x = newValue) else copy(y = newValue)
 
@@ -328,12 +334,7 @@
         pointerPosition: Offset?,
         source: NestedScrollSource
     ): Offset {
-        val overscrollPreConsumed =
-            if (overscrollEffect != null && overscrollEffect.isEnabled) {
-                overscrollEffect.consumePreScroll(scrollDelta, pointerPosition, source)
-            } else {
-                Offset.Zero
-            }
+        val overscrollPreConsumed = overscrollPreConsumeDelta(scrollDelta, pointerPosition, source)
 
         val afterPreOverscroll = scrollDelta - overscrollPreConsumed
         val nestedScrollDispatcher = nestedScrollDispatcher.value
@@ -349,15 +350,43 @@
         val leftForParent = scrollAvailable - axisConsumed
         val parentConsumed = nestedScrollDispatcher
             .dispatchPostScroll(axisConsumed, leftForParent, source)
+
+        overscrollPostConsumeDelta(
+            scrollAvailable,
+            leftForParent - parentConsumed,
+            pointerPosition,
+            source
+        )
+        return leftForParent
+    }
+
+    fun overscrollPreConsumeDelta(
+        scrollDelta: Offset,
+        pointerPosition: Offset?,
+        source: NestedScrollSource
+    ): Offset {
+        return if (overscrollEffect != null && overscrollEffect.isEnabled) {
+            val overscrollAvailableDelta = scrollDelta.singleAxisOffset()
+            overscrollEffect.consumePreScroll(overscrollAvailableDelta, pointerPosition, source)
+        } else {
+            Offset.Zero
+        }
+    }
+
+    private fun overscrollPostConsumeDelta(
+        consumedByChain: Offset,
+        availableForOverscroll: Offset,
+        pointerPosition: Offset?,
+        source: NestedScrollSource
+    ) {
         if (overscrollEffect != null && overscrollEffect.isEnabled) {
             overscrollEffect.consumePostScroll(
-                scrollAvailable,
-                (leftForParent - parentConsumed),
+                consumedByChain.singleAxisOffset(),
+                availableForOverscroll.singleAxisOffset(),
                 pointerPosition,
                 source
             )
         }
-        return leftForParent
     }
 
     fun performRawScroll(scroll: Offset): Offset {
@@ -372,7 +401,9 @@
     suspend fun onDragStopped(initialVelocity: Velocity) {
         val preOverscrollConsumed =
             if (overscrollEffect != null && overscrollEffect.isEnabled) {
-                overscrollEffect.consumePreFling(initialVelocity)
+                val availableForOverscroll = initialVelocity.singleAxisVelocity()
+                val crossAxisRemainder = initialVelocity - availableForOverscroll
+                crossAxisRemainder + overscrollEffect.consumePreFling(availableForOverscroll)
             } else {
                 Velocity.Zero
             }
@@ -387,7 +418,7 @@
             )
         val totalLeft = velocityLeft - consumedPost
         if (overscrollEffect != null && overscrollEffect.isEnabled) {
-            overscrollEffect.consumePostFling(totalLeft)
+            overscrollEffect.consumePostFling(totalLeft.singleAxisVelocity())
         }
     }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt
index abb13b2..a4fdebf 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/BasicText.kt
@@ -85,10 +85,13 @@
 
     // NOTE(text-perf-review): potential bug. selectableId is regenerated here whenever text
     // changes, but it is only saved in the initial creation of TextState.
-    val selectableId =
+    val selectableId = if (selectionRegistrar == null) {
+        SelectionRegistrar.InvalidSelectableId
+    } else {
         rememberSaveable(text, selectionRegistrar, saver = selectionIdSaver(selectionRegistrar)) {
-            selectionRegistrar?.nextSelectableId() ?: SelectionRegistrar.InvalidSelectableId
+            selectionRegistrar.nextSelectableId()
         }
+    }
 
     val controller = remember {
         TextController(
@@ -184,10 +187,13 @@
 
     // NOTE(text-perf-review): potential bug. selectableId is regenerated here whenever text
     // changes, but it is only saved in the initial creation of TextState.
-    val selectableId =
+    val selectableId = if (selectionRegistrar == null) {
+        SelectionRegistrar.InvalidSelectableId
+    } else {
         rememberSaveable(text, selectionRegistrar, saver = selectionIdSaver(selectionRegistrar)) {
-            selectionRegistrar?.nextSelectableId() ?: SelectionRegistrar.InvalidSelectableId
+            selectionRegistrar.nextSelectableId()
         }
+    }
 
     val controller = remember {
         TextController(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
index 6cfc11f..684df3b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/SelectionContainer.kt
@@ -34,7 +34,7 @@
 import androidx.compose.ui.util.fastForEach
 
 /**
- * Enables text selection for it's direct or indirection children.
+ * Enables text selection for it's direct or indirect children.
  *
  * @sample androidx.compose.foundation.samples.SelectionSample
  */
@@ -52,7 +52,7 @@
 }
 
 /**
- * Disables text selection for it's direct or indirection children. To use this, simply add this
+ * Disables text selection for it's direct or indirect children. To use this, simply add this
  * to wrap one or more text composables.
  *
  * @sample androidx.compose.foundation.samples.DisableSelectionSample
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderTest.kt
index 8912f1c..82bef6b 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderTest.kt
@@ -592,6 +592,34 @@
     }
 
     @Test
+    fun slider_setProgress_callsOnValueChangeFinished() {
+        val state = mutableStateOf(0f)
+        val callCount = mutableStateOf(0)
+
+        rule.setMaterialContent {
+            Slider(
+                modifier = Modifier.testTag(tag),
+                value = state.value,
+                onValueChangeFinished = {
+                    callCount.value += 1
+                },
+                onValueChange = { state.value = it }
+            )
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(callCount.value).isEqualTo(0)
+        }
+
+        rule.onNodeWithTag(tag)
+            .performSemanticsAction(SemanticsActions.SetProgress) { it(0.8f) }
+
+        rule.runOnIdle {
+            Truth.assertThat(callCount.value).isEqualTo(1)
+        }
+    }
+
+    @Test
     fun slider_interactionSource_resetWhenDisposed() {
         val interactionSource = MutableInteractionSource()
         var emitSlider by mutableStateOf(true)
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
index 339aa65..580ab3a 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
@@ -163,7 +163,14 @@
         modifier
             .minimumTouchTargetSize()
             .requiredSizeIn(minWidth = ThumbRadius * 2, minHeight = ThumbRadius * 2)
-            .sliderSemantics(value, enabled, onValueChange, valueRange, steps)
+            .sliderSemantics(
+                value,
+                enabled,
+                onValueChange,
+                onValueChangeFinished,
+                valueRange,
+                steps
+            )
             .focusable(enabled, interactionSource)
     ) {
         val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
@@ -407,6 +414,7 @@
             coercedStart,
             enabled,
             { value -> onValueChangeState.value.invoke(value..coercedEnd) },
+            onValueChangeFinished,
             valueRange.start..coercedEnd,
             startSteps
         )
@@ -414,6 +422,7 @@
             coercedEnd,
             enabled,
             { value -> onValueChangeState.value.invoke(coercedStart..value) },
+            onValueChangeFinished,
             coercedStart..valueRange.endInclusive,
             endSteps
         )
@@ -842,6 +851,7 @@
     value: Float,
     enabled: Boolean,
     onValueChange: (Float) -> Unit,
+    onValueChangeFinished: (() -> Unit)? = null,
     valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
     steps: Int = 0
 ): Modifier {
@@ -874,6 +884,7 @@
                     false
                 } else {
                     onValueChange(resolvedValue)
+                    onValueChangeFinished?.invoke()
                     true
                 }
             }
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarTest.kt
index 8f0d760..d708735 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/NavigationBarTest.kt
@@ -215,20 +215,16 @@
         val itemBounds = rule.onNodeWithTag("item").getUnclippedBoundsInRoot()
         val iconBounds = rule.onNodeWithTag("icon", useUnmergedTree = true)
             .getUnclippedBoundsInRoot()
-        val textBounds = rule.onNodeWithText("ItemText").getUnclippedBoundsInRoot()
+        val textBounds = rule.onNodeWithText("ItemText", useUnmergedTree = true)
+            .getUnclippedBoundsInRoot()
 
-        // Distance from the bottom to the text baseline, and from the top of the icon to the
-        // top of the item
+        // Distance from the bottom of the item to the text bottom, and from the top of the icon to
+        // the top of the item
         val verticalPadding = NavigationBarItemVerticalPadding
 
-        // Relative position of the baseline to the top of text
-        val relativeTextBaseline = rule.onNodeWithText("ItemText").getLastBaselinePosition()
-        // Absolute y position of the text baseline
-        val absoluteTextBaseline = textBounds.top + relativeTextBaseline
-
         val itemBottom = itemBounds.height + itemBounds.top
-        // Text baseline should be `verticalPadding` from the bottom of the item
-        absoluteTextBaseline.assertIsEqualTo(itemBottom - verticalPadding)
+        // Text bottom should be `verticalPadding` from the bottom of the item
+        textBounds.bottom.assertIsEqualTo(itemBottom - verticalPadding)
 
         rule.onNodeWithTag("icon", useUnmergedTree = true)
             // The icon should be centered in the item
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt
index 95ef9ed..93782c2 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SliderTest.kt
@@ -522,6 +522,34 @@
     }
 
     @Test
+    fun slider_setProgress_callsOnValueChangeFinished() {
+        val state = mutableStateOf(0f)
+        val callCount = mutableStateOf(0)
+
+        rule.setMaterialContent(lightColorScheme()) {
+            Slider(
+                modifier = Modifier.testTag(tag),
+                value = state.value,
+                onValueChangeFinished = {
+                    callCount.value += 1
+                },
+                onValueChange = { state.value = it }
+            )
+        }
+
+        rule.runOnIdle {
+            Truth.assertThat(callCount.value).isEqualTo(0)
+        }
+
+        rule.onNodeWithTag(tag)
+            .performSemanticsAction(SemanticsActions.SetProgress) { it(0.8f) }
+
+        rule.runOnIdle {
+            Truth.assertThat(callCount.value).isEqualTo(1)
+        }
+    }
+
+    @Test
     fun slider_interactionSource_resetWhenDisposed() {
         val interactionSource = MutableInteractionSource()
         var emitSlider by mutableStateOf(true)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
index c468caa..42541a1 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
@@ -48,7 +48,6 @@
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.LastBaseline
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.MeasureScope
@@ -503,9 +502,8 @@
 ): MeasureResult {
     val height = constraints.maxHeight
 
-    val baseline = labelPlaceable[LastBaseline]
     // Label should be `ItemVerticalPadding` from the bottom
-    val labelY = height - baseline - NavigationBarItemVerticalPadding.roundToPx()
+    val labelY = height - labelPlaceable.height - NavigationBarItemVerticalPadding.roundToPx()
 
     // Icon (when selected) should be `ItemVerticalPadding` from the top
     val selectedIconY = NavigationBarItemVerticalPadding.roundToPx()
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
index d542236..dd9b2d9 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
@@ -173,7 +173,14 @@
                 minWidth = SliderTokens.HandleWidth,
                 minHeight = SliderTokens.HandleHeight
             )
-            .sliderSemantics(value, enabled, onValueChange, valueRange, steps)
+            .sliderSemantics(
+                value,
+                enabled,
+                onValueChange,
+                onValueChangeFinished,
+                valueRange,
+                steps
+            )
             .focusable(enabled, interactionSource)
     ) {
         val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
@@ -378,6 +385,7 @@
             coercedStart,
             enabled,
             { value -> onValueChangeState.value.invoke(value..coercedEnd) },
+            onValueChangeFinished,
             valueRange.start..coercedEnd,
             startSteps
         )
@@ -385,6 +393,7 @@
             coercedEnd,
             enabled,
             { value -> onValueChangeState.value.invoke(coercedStart..value) },
+            onValueChangeFinished,
             coercedStart..valueRange.endInclusive,
             endSteps
         )
@@ -781,6 +790,7 @@
     value: Float,
     enabled: Boolean,
     onValueChange: (Float) -> Unit,
+    onValueChangeFinished: (() -> Unit)? = null,
     valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
     steps: Int = 0
 ): Modifier {
@@ -815,6 +825,7 @@
                     false
                 } else {
                     onValueChange(resolvedValue)
+                    onValueChangeFinished?.invoke()
                     true
                 }
             }
diff --git a/compose/runtime/runtime-tracing/api/current.txt b/compose/runtime/runtime-tracing/api/current.txt
new file mode 100644
index 0000000..716d534
--- /dev/null
+++ b/compose/runtime/runtime-tracing/api/current.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.compose.runtime.tracing {
+
+  public final class TracingInitializer implements androidx.startup.Initializer<kotlin.Unit> {
+    ctor public TracingInitializer();
+    method public void create(android.content.Context context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>> dependencies();
+  }
+
+}
+
diff --git a/compose/runtime/runtime-tracing/api/public_plus_experimental_current.txt b/compose/runtime/runtime-tracing/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..716d534
--- /dev/null
+++ b/compose/runtime/runtime-tracing/api/public_plus_experimental_current.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.compose.runtime.tracing {
+
+  public final class TracingInitializer implements androidx.startup.Initializer<kotlin.Unit> {
+    ctor public TracingInitializer();
+    method public void create(android.content.Context context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>> dependencies();
+  }
+
+}
+
diff --git a/compose/runtime/runtime-tracing/api/res-current.txt b/compose/runtime/runtime-tracing/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/compose/runtime/runtime-tracing/api/res-current.txt
diff --git a/compose/runtime/runtime-tracing/api/restricted_current.txt b/compose/runtime/runtime-tracing/api/restricted_current.txt
new file mode 100644
index 0000000..716d534
--- /dev/null
+++ b/compose/runtime/runtime-tracing/api/restricted_current.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.compose.runtime.tracing {
+
+  public final class TracingInitializer implements androidx.startup.Initializer<kotlin.Unit> {
+    ctor public TracingInitializer();
+    method public void create(android.content.Context context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>> dependencies();
+  }
+
+}
+
diff --git a/compose/runtime/runtime-tracing/build.gradle b/compose/runtime/runtime-tracing/build.gradle
index 5211b24..242290f 100644
--- a/compose/runtime/runtime-tracing/build.gradle
+++ b/compose/runtime/runtime-tracing/build.gradle
@@ -49,12 +49,11 @@
 
 androidx {
     name = "Compose Runtime: Tracing"
-    runApiTasks= new RunApiTasks.No("The API is still evolving") // TODO(b/231444429)
     publish = Publish.SNAPSHOT_AND_RELEASE
     mavenVersion = LibraryVersions.COMPOSE_RUNTIME_TRACING
     mavenGroup = LibraryGroups.COMPOSE_RUNTIME
     inceptionYear = "2022"
-    description = "Allows for additional tracing in a Compose app (e.g. tracing of Composables taking part in a Recomposition"
+    description = "Additional tracing in Compose"
 }
 
 tasks.withType(KotlinCompile).configureEach {
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposePreciseFingerTapIntegrationBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposePreciseFingerTapIntegrationBenchmark.kt
new file mode 100644
index 0000000..2e80a90
--- /dev/null
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposePreciseFingerTapIntegrationBenchmark.kt
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.benchmark.input.pointer
+
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.ComposeTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.doFramesUntilNoChangesPending
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Benchmark for precise finger tapping (down, move, and up) on an item in Compose created from a
+ * real device.
+ *
+ * The intent is to measure the speed of all parts necessary for a normal finger tap and move
+ * starting from [MotionEvent]s getting dispatched to a particular view.  The test therefore
+ * includes hit testing and dispatch.
+ *
+ * This is intended to be a more through benchmark of [ComposeTapIntegrationBenchmark] and a finger
+ * tapping version of [ComposePreciseStylusTapIntegrationBenchmark].
+ *
+ * The hierarchy is set up to look like:
+ * rootView
+ *   -> Column
+ *     -> Text (with click listener)
+ *     -> Text (with click listener)
+ *     -> Text (with click listener)
+ *     -> ...
+ *
+ * MotionEvents are dispatched to rootView as an ACTION_DOWN, an ACTION_MOVE, and finally
+ * an ACTION_UP.  The validity of the test is verified inside the click listener with
+ * com.google.common.truth.Truth.assertThat and by counting the clicks in the click listener and
+ * later verifying that they count is sufficiently high.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class ComposePreciseFingerTapIntegrationBenchmark {
+
+    @get:Rule
+    val benchmarkRule = ComposeBenchmarkRule()
+
+    @Test
+    fun clickOnLateItem() {
+        // As items that are laid out last are hit tested first (so z order is respected), item
+        // at 0 will be hit tested late.
+        clickOnItem(0, "0")
+    }
+
+    // This test requires less hit testing so changes to dispatch will be tracked more by this test.
+    @Test
+    fun clickOnEarlyItemFyi() {
+        // As items that are laid out last are hit tested first (so z order is respected), item
+        // at NumItems - 1 will be hit tested early.
+        val lastItem = NumItems - 1
+        clickOnItem(lastItem, "$lastItem")
+    }
+
+    private fun clickOnItem(item: Int, expectedLabel: String) {
+        val xDown = 0f
+        // half height of an item + top of the chosen item = middle of the chosen item
+        val yDown = (ItemHeightPx / 2) + (item * ItemHeightPx)
+
+        val xMove = xDown + MOVE_AMOUNT_PX
+        val yMove = yDown + MOVE_AMOUNT_PX
+
+        val xUp = xMove + MOVE_AMOUNT_PX
+        val yUp = yMove + MOVE_AMOUNT_PX
+
+        benchmarkRule.runBenchmarkFor({ ComposeTapTestCase() }) {
+            doFramesUntilNoChangesPending()
+
+            val case = getTestCase()
+            case.expectedLabel = expectedLabel
+
+            val rootView = getHostView()
+
+            // Precise Touch/Finger MotionEvents (Down, Move, Up)
+            // Based on real MotionEvents pulled from a device.
+            val fingerDownMotionEvent = android.view.MotionEvent.obtain(
+                8451548L,
+                8451548L,
+                android.view.MotionEvent.ACTION_DOWN,
+                1,
+                arrayOf(
+                    PointerProperties(0).apply {
+                        toolType = android.view.MotionEvent.TOOL_TYPE_FINGER
+                    }
+                ),
+                arrayOf(
+                    PointerCoords(xDown, yDown).apply {
+                        pressure = 1.0f
+                        size = 0.08639053f
+                    }
+                ),
+                0,
+                0,
+                1.000625f,
+                1.0003906f,
+                6,
+                0x0, // Edge Flags value of 0.
+                0x1002, // Source of the event value of 4098
+                0x2 // Motion Event Flags value of 2
+            )
+
+            val fingerMoveMotionEvent = android.view.MotionEvent.obtain(
+                8451548L,
+                8451632L,
+                android.view.MotionEvent.ACTION_MOVE,
+                1,
+                arrayOf(
+                    PointerProperties(0).apply {
+                        toolType = android.view.MotionEvent.TOOL_TYPE_FINGER
+                    }
+                ),
+                arrayOf(
+                    PointerCoords(xMove, yMove).apply {
+                        pressure = 1.0f
+                        size = 0.08639053f
+                    }
+                ),
+                0,
+                0,
+                1.000625f,
+                1.0003906f,
+                6,
+                0x0, // Edge Flags value of 0.
+                0x1002, // Source of the event value of 4098
+                0x2 // Motion Event Flags value of 2
+            )
+
+            val fingerUpMotionEvent = android.view.MotionEvent.obtain(
+                8451548L,
+                8451756L,
+                android.view.MotionEvent.ACTION_UP,
+                1,
+                arrayOf(
+                    PointerProperties(0).apply {
+                        toolType = android.view.MotionEvent.TOOL_TYPE_FINGER
+                    }
+                ),
+                arrayOf(
+                    PointerCoords(xUp, yUp).apply {
+                        pressure = 1.0f
+                        size = 0.08639053f
+                    }
+                ),
+                0,
+                0,
+                1.000625f,
+                1.0003906f,
+                6,
+                0x0, // Edge Flags value of 0.
+                0x1002, // Source of the event value of 4098
+                0x2 // Motion Event Flags value of 2
+            )
+
+            benchmarkRule.measureRepeated {
+
+                rootView.dispatchTouchEvent(fingerDownMotionEvent)
+                rootView.dispatchTouchEvent(fingerMoveMotionEvent)
+                rootView.dispatchTouchEvent(fingerUpMotionEvent)
+
+                case.expectedClickCount++
+                assertThat(case.actualClickCount).isEqualTo(case.expectedClickCount)
+            }
+        }
+    }
+
+    private class ComposeTapTestCase : ComposeTestCase {
+        private var itemHeightDp = 0.dp // Is set to correct value during composition.
+        var actualClickCount = 0
+        var expectedClickCount = 0
+        lateinit var expectedLabel: String
+
+        @Composable
+        override fun Content() {
+            with(LocalDensity.current) {
+                itemHeightDp = ItemHeightPx.toDp()
+            }
+
+            EmailList(NumItems)
+        }
+
+        @Composable
+        fun EmailList(count: Int) {
+            Column {
+                repeat(count) { i ->
+                    Email("$i")
+                }
+            }
+        }
+
+        @Composable
+        fun Email(label: String) {
+            BasicText(
+                text = label,
+                modifier = Modifier
+                    .pointerInput(label) {
+                        detectTapGestures {
+                            assertThat(label).isEqualTo(expectedLabel)
+                            actualClickCount++
+                        }
+                    }
+                    .fillMaxWidth()
+                    .requiredHeight(itemHeightDp)
+            )
+        }
+    }
+    companion object {
+        private const val MOVE_AMOUNT_PX = 30f
+    }
+}
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposePreciseStylusTapIntegrationBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposePreciseStylusTapIntegrationBenchmark.kt
new file mode 100644
index 0000000..906d65d
--- /dev/null
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposePreciseStylusTapIntegrationBenchmark.kt
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.benchmark.input.pointer
+
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.waitForUpOrCancellation
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.ComposeTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.doFramesUntilNoChangesPending
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.isActive
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Benchmark for precise stylus tapping (down, move, and up) on an item in Compose created from a
+ * real device.
+ *
+ * The intent is to measure the speed of all parts necessary for a normal stylus tap and move
+ * starting from [MotionEvent]s getting dispatched to a particular view.  The test therefore
+ * includes hit testing and dispatch.
+ *
+ * This is intended to be a more through benchmark of [ComposeTapIntegrationBenchmark] and a stylus
+ * version of [ComposePreciseFingerTapIntegrationBenchmark].
+ *
+ * The hierarchy is set up to look like:
+ * rootView
+ *   -> Column
+ *     -> Text (with click listener)
+ *     -> Text (with click listener)
+ *     -> Text (with click listener)
+ *     -> ...
+ *
+ * MotionEvents are dispatched to rootView as an ACTION_DOWN, an ACTION_MOVE, and finally
+ * an ACTION_UP.  The validity of the test is verified inside the click listener with
+ * com.google.common.truth.Truth.assertThat and by counting the clicks in the click listener and
+ * later verifying that they count is sufficiently high.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class ComposePreciseStylusTapIntegrationBenchmark {
+
+    @get:Rule
+    val benchmarkRule = ComposeBenchmarkRule()
+
+    @Test
+    fun clickOnLateItem() {
+        // As items that are laid out last are hit tested first (so z order is respected), item
+        // at 0 will be hit tested late.
+        clickOnItem(0, "0")
+    }
+
+    // This test requires less hit testing so changes to dispatch will be tracked more by this test.
+    @Test
+    fun clickOnEarlyItemFyi() {
+        // As items that are laid out last are hit tested first (so z order is respected), item
+        // at NumItems - 1 will be hit tested early.
+        val lastItem = NumItems - 1
+        clickOnItem(lastItem, "$lastItem")
+    }
+
+    private fun clickOnItem(item: Int, expectedLabel: String) {
+        val xDown = 0f
+        // half height of an item + top of the chosen item = middle of the chosen item
+        val yDown = (ItemHeightPx / 2) + (item * ItemHeightPx)
+
+        val xMove = xDown + MOVE_AMOUNT_PX
+        val yMove = yDown + MOVE_AMOUNT_PX
+
+        val xUp = xMove + MOVE_AMOUNT_PX
+        val yUp = yMove + MOVE_AMOUNT_PX
+
+        benchmarkRule.runBenchmarkFor({ ComposeTapTestCase() }) {
+            doFramesUntilNoChangesPending()
+
+            val case = getTestCase()
+            case.expectedLabel = expectedLabel
+
+            val rootView = getHostView()
+
+            // Precise Stylus MotionEvents (Down, Move, Up)
+            // Based on real MotionEvents pulled from a device.
+            val stylusDownMotionEvent = android.view.MotionEvent.obtain(
+                346709L,
+                346709L,
+                android.view.MotionEvent.ACTION_DOWN,
+                1,
+                arrayOf(
+                    PointerProperties(0).apply {
+                        toolType = android.view.MotionEvent.TOOL_TYPE_STYLUS
+                    }
+                ),
+                arrayOf(
+                    PointerCoords(xDown, yDown).apply {
+                        pressure = 0.18339439f
+                        size = 0.0f
+                    }
+                ),
+                0,
+                0,
+                1.000625f,
+                1.0003906f,
+                7,
+                0x0, // Edge Flags value of 0.
+                0x5002, // Source of the event value of 20482
+                0x2 // Motion Event Flags value of 2
+            )
+
+            val stylusMoveMotionEvent = android.view.MotionEvent.obtain(
+                346709L,
+                347222L,
+                android.view.MotionEvent.ACTION_MOVE,
+                1,
+                arrayOf(
+                    PointerProperties(0).apply {
+                        toolType = android.view.MotionEvent.TOOL_TYPE_STYLUS
+                    }
+                ),
+                arrayOf(
+                    PointerCoords(xMove, yMove).apply {
+                        pressure = 0.2947497f
+                        size = 0.0f
+                    }
+                ),
+                0,
+                0,
+                1.000625f,
+                1.0003906f,
+                7,
+                0x0, // Edge Flags value of 0.
+                0x5002, // Source of the event value of 20482
+                0x2 // Motion Event Flags value of 2
+            )
+
+            val stylusUpMotionEvent = android.view.MotionEvent.obtain(
+                346709L,
+                347227L,
+                android.view.MotionEvent.ACTION_UP,
+                1,
+                arrayOf(
+                    PointerProperties(0).apply {
+                        toolType = android.view.MotionEvent.TOOL_TYPE_STYLUS
+                    }
+                ),
+                arrayOf(
+                    PointerCoords(xUp, yUp).apply {
+                        pressure = 0.2947497f
+                        size = 0.0f
+                    }
+                ),
+                0,
+                0,
+                1.000625f,
+                1.0003906f,
+                7,
+                0x0, // Edge Flags value of 0.
+                0x5002, // Source of the event value of 20482
+                0x2 // Motion Event Flags value of 2
+            )
+
+            benchmarkRule.measureRepeated {
+
+                rootView.dispatchTouchEvent(stylusDownMotionEvent)
+                rootView.dispatchTouchEvent(stylusMoveMotionEvent)
+                rootView.dispatchTouchEvent(stylusUpMotionEvent)
+
+                case.expectedClickCount++
+                assertThat(case.actualClickCount).isEqualTo(case.expectedClickCount)
+            }
+        }
+    }
+
+    private class ComposeTapTestCase : ComposeTestCase {
+        private var itemHeightDp = 0.dp // Is set to correct value during composition.
+        var actualClickCount = 0
+        var expectedClickCount = 0
+        lateinit var expectedLabel: String
+
+        @Composable
+        override fun Content() {
+            with(LocalDensity.current) {
+                itemHeightDp = ItemHeightPx.toDp()
+            }
+
+            EmailList(NumItems)
+        }
+
+        @Composable
+        fun EmailList(count: Int) {
+            Column {
+                repeat(count) { i ->
+                    Email("$i")
+                }
+            }
+        }
+
+        @Composable
+        fun Email(label: String) {
+            BasicText(
+                text = label,
+                modifier = Modifier
+                    .pointerInput(label) {
+                        coroutineScope {
+                            awaitPointerEventScope {
+                                while (coroutineContext.isActive) {
+                                    val down = awaitFirstDown()
+                                    down.consume()
+
+                                    val upOrCancel: PointerInputChange? = waitForUpOrCancellation()
+                                    upOrCancel?.consume()
+                                    assertThat(label).isEqualTo(expectedLabel)
+                                    actualClickCount++
+                                }
+                            }
+                        }
+                    }
+                    .fillMaxWidth()
+                    .requiredHeight(itemHeightDp)
+            )
+        }
+    }
+    companion object {
+        private const val MOVE_AMOUNT_PX = 30f
+    }
+}
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeTapIntegrationBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeTapIntegrationBenchmark.kt
index f8cf39f..3ce42b3 100644
--- a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeTapIntegrationBenchmark.kt
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/input/pointer/ComposeTapIntegrationBenchmark.kt
@@ -93,6 +93,7 @@
 
             val rootView = getHostView()
 
+            // Simple Events
             val down = MotionEvent(
                 0,
                 android.view.MotionEvent.ACTION_DOWN,
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifierTest.kt
index 780227e..2d5bf83 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifierTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/nestedscroll/NestedScrollModifierTest.kt
@@ -17,22 +17,46 @@
 package androidx.compose.ui.input.nestedscroll
 
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.background
 import androidx.compose.ui.draw.clipToBounds
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.platform.AbstractComposeView
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.CoordinatesProvider
+import androidx.test.espresso.action.GeneralLocation
+import androidx.test.espresso.action.GeneralSwipeAction
+import androidx.test.espresso.action.Press
+import androidx.test.espresso.action.Swipe
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import kotlin.math.abs
+import kotlin.math.sign
 import kotlinx.coroutines.isActive
 import kotlinx.coroutines.runBlocking
+import org.hamcrest.CoreMatchers.allOf
+import org.hamcrest.CoreMatchers.instanceOf
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -961,7 +985,133 @@
         }
     }
 
-    // helper functions
+    @Test
+    fun nestedScroll_movingTarget_velocityShouldRespectSign() {
+        var lastVelocity = Velocity.Zero
+        val MaxOffsetBound = 900f
+        val ConsumedEverything = 0.0f
+        rule.setContent {
+            var offset by remember {
+                mutableStateOf(MaxOffsetBound)
+            }
+            val nestedScrollConnection = remember {
+                object : NestedScrollConnection {
+                    fun consumedDelta(scrollDelta: Float): Float {
+                        if (offset == 0f && scrollDelta < 0f) return ConsumedEverything
+                        if (offset == MaxOffsetBound && scrollDelta > 0f) return ConsumedEverything
+                        val previousOffset = offset
+                        offset = (scrollDelta + offset).coerceIn(0f, MaxOffsetBound)
+                        return offset - previousOffset
+                    }
+
+                    override fun onPreScroll(
+                        available: Offset,
+                        source: NestedScrollSource
+                    ): Offset {
+                        return if (available.y < 0) {
+                            val consumed = consumedDelta(available.y)
+                            Offset(x = 0f, y = consumed)
+                        } else Offset.Zero
+                    }
+
+                    override fun onPostScroll(
+                        consumed: Offset,
+                        available: Offset,
+                        source: NestedScrollSource
+                    ): Offset {
+                        return if (abs(available.y) > 0f &&
+                            available.y > 0f
+                        ) {
+                            Offset(0f, consumedDelta(available.y))
+                        } else
+                            super.onPostScroll(consumed, available, source)
+                    }
+
+                    override suspend fun onPreFling(available: Velocity): Velocity {
+                        lastVelocity = available
+                        return super.onPreFling(available)
+                    }
+                }
+            }
+
+            Column(modifier = Modifier.fillMaxSize()) {
+                Box(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .height(80.dp)
+                )
+                LazyColumn(
+                    modifier = Modifier
+                        .graphicsLayer {
+                            translationY = offset
+                        }
+                        .nestedScroll(connection = nestedScrollConnection)
+                        .fillMaxWidth()
+                        .weight(1f)
+                ) {
+                    items(100) {
+                        Box(
+                            modifier = Modifier
+                                .fillMaxWidth()
+                                .height(60.dp)
+                                .background(Color.Gray)
+                        ) {
+                            BasicText(text = it.toString())
+                        }
+                        Spacer(modifier = Modifier.height(8.dp))
+                    }
+                }
+            }
+        }
+
+        composeViewSwipeUp()
+        rule.runOnIdle {
+            // swipe ups provide negative signed velocities
+            assertThat(sign(lastVelocity.y)).isEqualTo(-1)
+        }
+        composeViewSwipeDown()
+        rule.runOnIdle {
+            // swipe downs provide positive signed velocities
+            assertThat(sign(lastVelocity.y)).isEqualTo(1)
+        }
+        composeViewSwipeDown()
+        rule.runOnIdle {
+            // swipe downs provide positive signed velocities
+            assertThat(sign(lastVelocity.y)).isEqualTo(1)
+        }
+    }
+
+// helper functions
+
+    private fun composeViewSwipeUp() {
+        onView(allOf(instanceOf(AbstractComposeView::class.java)))
+            .perform(
+                espressoSwipe(
+                    GeneralLocation.BOTTOM_CENTER,
+                    GeneralLocation.CENTER
+                )
+            )
+    }
+
+    private fun composeViewSwipeDown() {
+        onView(allOf(instanceOf(AbstractComposeView::class.java)))
+            .perform(
+                espressoSwipe(
+                    GeneralLocation.CENTER,
+                    GeneralLocation.BOTTOM_CENTER
+                )
+            )
+    }
+
+    private fun espressoSwipe(
+        start: CoordinatesProvider,
+        end: CoordinatesProvider
+    ): GeneralSwipeAction {
+        return GeneralSwipeAction(
+            Swipe.FAST, start, end,
+            Press.FINGER
+        )
+    }
 
     private fun testMiddleParentAdditionRemoval(
         content: @Composable (root: Modifier, middle: Modifier, child: Modifier) -> Unit
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/util/VelocityTracker.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/util/VelocityTracker.kt
index 869c0e6..dc8372e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/util/VelocityTracker.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/util/VelocityTracker.kt
@@ -19,6 +19,7 @@
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.util.fastForEach
 import kotlin.math.abs
@@ -26,6 +27,7 @@
 
 private const val AssumePointerMoveStoppedMilliseconds: Int = 40
 private const val HistorySize: Int = 20
+
 // TODO(b/204895043): Keep value in sync with VelocityPathFinder.HorizonMilliSeconds
 private const val HorizonMilliseconds: Int = 100
 private const val MinSampleSize: Int = 3
@@ -47,6 +49,7 @@
     // Circular buffer; current sample at index.
     private val samples: Array<PointAtTime?> = Array(HistorySize) { null }
     private var index: Int = 0
+    internal var currentPointerPositionAccumulator = Offset.Zero
 
     /**
      * Adds a position as the given time to the tracker.
@@ -167,14 +170,48 @@
  * For optimal tracking, this should be called for the DOWN event and all MOVE
  * events, including any touch-slop-captured MOVE event.
  *
+ * Since Compose uses relative positions inside PointerInputChange, this should be
+ * taken into consideration when using this method. Right now, we use the first down
+ * to initialize an accumulator and use subsequent deltas to simulate an actual movement
+ * from relative positions in PointerInputChange. This is required because VelocityTracker
+ * requires data that can be fit into a curve, which might not happen with relative positions
+ * inside a moving target for instance.
+ *
  * @param event Pointer change to track.
  */
 fun VelocityTracker.addPointerInputChange(event: PointerInputChange) {
+
+    // Register down event as the starting point for the accumulator
+    if (event.changedToDownIgnoreConsumed()) {
+        currentPointerPositionAccumulator = event.position
+        resetTracking()
+    }
+
+    // To calculate delta, for each step we want to  do currentPosition - previousPosition.
+    // Initially the previous position is the previous position of the current event
+    var previousPointerPosition = event.previousPosition
     @OptIn(ExperimentalComposeUiApi::class)
     event.historical.fastForEach {
-        addPosition(it.uptimeMillis, it.position)
+        // Historical data happens within event.position and event.previousPosition
+        // That means, event.previousPosition < historical data < event.position
+        // Initially, the first delta will happen between the previousPosition and
+        // the first position in historical delta. For subsequent historical data, the
+        // deltas happen between themselves. That's why we need to update previousPointerPosition
+        // everytime.
+        val historicalDelta = it.position - previousPointerPosition
+        previousPointerPosition = it.position
+
+        // Update the current position with the historical delta and add it to the tracker
+        currentPointerPositionAccumulator += historicalDelta
+        addPosition(it.uptimeMillis, currentPointerPositionAccumulator)
     }
-    addPosition(event.uptimeMillis, event.position)
+
+    // For the last position in the event
+    // If there's historical data, the delta is event.position - lastHistoricalPoint
+    // If there's no historical data, the delta is event.position - event.previousPosition
+    val delta = event.position - previousPointerPosition
+    currentPointerPositionAccumulator += delta
+    addPosition(event.uptimeMillis, currentPointerPositionAccumulator)
 }
 
 private data class PointAtTime(val point: Offset, val time: Long)
@@ -182,7 +219,7 @@
 /**
  * A two dimensional velocity estimate.
  *
- * VelocityEstimates are computed by [VelocityTracker.getVelocityEstimate]. An
+ * VelocityEstimates are computed by [VelocityTracker.getImpulseVelocity]. An
  * estimate's [confidence] measures how well the velocity tracker's position
  * data fit a straight line, [durationMillis] is the time that elapsed between the
  * first and last position sample used to compute the velocity, and [offset]
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index c02cb38..bb46e53 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -1388,7 +1388,7 @@
     method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
     method public static void setTint(android.graphics.drawable.Drawable, @ColorInt int);
     method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList?);
-    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode);
+    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode?);
     method public static <T extends android.graphics.drawable.Drawable> T! unwrap(android.graphics.drawable.Drawable);
     method public static android.graphics.drawable.Drawable wrap(android.graphics.drawable.Drawable);
   }
diff --git a/core/core/api/public_plus_experimental_current.txt b/core/core/api/public_plus_experimental_current.txt
index 191403e..f6099ee 100644
--- a/core/core/api/public_plus_experimental_current.txt
+++ b/core/core/api/public_plus_experimental_current.txt
@@ -1388,7 +1388,7 @@
     method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
     method public static void setTint(android.graphics.drawable.Drawable, @ColorInt int);
     method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList?);
-    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode);
+    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode?);
     method public static <T extends android.graphics.drawable.Drawable> T! unwrap(android.graphics.drawable.Drawable);
     method public static android.graphics.drawable.Drawable wrap(android.graphics.drawable.Drawable);
   }
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 3a6cb54..615169b 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -1660,7 +1660,7 @@
     method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
     method public static void setTint(android.graphics.drawable.Drawable, @ColorInt int);
     method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList?);
-    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode);
+    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode?);
     method public static <T extends android.graphics.drawable.Drawable> T! unwrap(android.graphics.drawable.Drawable);
     method public static android.graphics.drawable.Drawable wrap(android.graphics.drawable.Drawable);
   }
diff --git a/core/core/src/main/java/androidx/core/graphics/drawable/DrawableCompat.java b/core/core/src/main/java/androidx/core/graphics/drawable/DrawableCompat.java
index 8e96af8..d738031 100644
--- a/core/core/src/main/java/androidx/core/graphics/drawable/DrawableCompat.java
+++ b/core/core/src/main/java/androidx/core/graphics/drawable/DrawableCompat.java
@@ -161,7 +161,7 @@
      * @param drawable The Drawable against which to invoke the method.
      * @param tintMode A Porter-Duff blending mode
      */
-    public static void setTintMode(@NonNull Drawable drawable, @NonNull PorterDuff.Mode tintMode) {
+    public static void setTintMode(@NonNull Drawable drawable, @Nullable PorterDuff.Mode tintMode) {
         if (Build.VERSION.SDK_INT >= 21) {
             Api21Impl.setTintMode(drawable, tintMode);
         } else if (drawable instanceof TintAwareDrawable) {
diff --git a/development/build_log_simplifier/message-flakes.ignore b/development/build_log_simplifier/message-flakes.ignore
index 9a3231a..15a5ba5 100644
--- a/development/build_log_simplifier/message-flakes.ignore
+++ b/development/build_log_simplifier/message-flakes.ignore
@@ -145,3 +145,5 @@
 Using custom version .* of AGP due to GRADLE_PLUGIN_VERSION being set\.
 Using custom version .* of Lint due to LINT_VERSION being set\.
 Using custom version .* of metalava due to METALAVA_VERSION being set\.
+Publishing build scan\.\.\.
+https://ge\.androidx\.dev/s/.*
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 8765e12..d0f6958 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -45,8 +45,8 @@
     docs("androidx.car.app:app-projected:1.2.0-rc01")
     docs("androidx.car.app:app-testing:1.2.0-rc01")
     docs("androidx.cardview:cardview:1.0.0")
-    docs("androidx.collection:collection:1.2.0")
-    docs("androidx.collection:collection-ktx:1.2.0")
+    docs("androidx.collection:collection:1.3.0-alpha01")
+    docs("androidx.collection:collection-ktx:1.3.0-alpha01")
     docs("androidx.compose.animation:animation:1.3.0-alpha01")
     docs("androidx.compose.animation:animation-core:1.3.0-alpha01")
     docs("androidx.compose.animation:animation-graphics:1.3.0-alpha01")
@@ -222,7 +222,7 @@
     docs("androidx.print:print:1.1.0-beta01")
     docs("androidx.profileinstaller:profileinstaller:1.2.0-rc01")
     docs("androidx.recommendation:recommendation:1.0.0")
-    docs("androidx.recyclerview:recyclerview:1.3.0-alpha02")
+    docs("androidx.recyclerview:recyclerview:1.3.0-beta01")
     docs("androidx.recyclerview:recyclerview-selection:2.0.0-alpha01")
     docs("androidx.remotecallback:remotecallback:1.0.0-alpha02")
     docs("androidx.resourceinspection:resourceinspection-annotation:1.0.1")
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index c3f07ca..05902486 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,3 +3,4 @@
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
 distributionUrl=../../../../tools/external/gradle/gradle-7.5-rc-2-bin.zip
+distributionSha256Sum=ba761aa1563f5d893d1a6e7ddca48bbdc4385fdd527974e6472b873b7eae9a32
diff --git a/health/health-connect-client/api/current.txt b/health/health-connect-client/api/current.txt
index 100f938..a4038e0 100644
--- a/health/health-connect-client/api/current.txt
+++ b/health/health-connect-client/api/current.txt
@@ -230,13 +230,13 @@
   }
 
   public final class BodyFatRecord implements androidx.health.connect.client.records.Record {
-    ctor public BodyFatRecord(int percentage, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public BodyFatRecord(androidx.health.connect.client.units.Percentage percentage, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public int getPercentage();
+    method public androidx.health.connect.client.units.Percentage getPercentage();
     method public java.time.Instant getTime();
     method public java.time.ZoneOffset? getZoneOffset();
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public final int percentage;
+    property public final androidx.health.connect.client.units.Percentage percentage;
     property public java.time.Instant time;
     property public java.time.ZoneOffset? zoneOffset;
   }
@@ -910,13 +910,13 @@
   }
 
   public final class OxygenSaturationRecord implements androidx.health.connect.client.records.Record {
-    ctor public OxygenSaturationRecord(int percentage, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public OxygenSaturationRecord(androidx.health.connect.client.units.Percentage percentage, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public int getPercentage();
+    method public androidx.health.connect.client.units.Percentage getPercentage();
     method public java.time.Instant getTime();
     method public java.time.ZoneOffset? getZoneOffset();
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public final int percentage;
+    property public final androidx.health.connect.client.units.Percentage percentage;
     property public java.time.Instant time;
     property public java.time.ZoneOffset? zoneOffset;
   }
@@ -1493,6 +1493,16 @@
   public final class MassKt {
   }
 
+  public final class Percentage implements java.lang.Comparable<androidx.health.connect.client.units.Percentage> {
+    ctor public Percentage(double value);
+    method public int compareTo(androidx.health.connect.client.units.Percentage other);
+    method public double getValue();
+    property public final double value;
+  }
+
+  public final class PercentageKt {
+  }
+
   public final class Power implements java.lang.Comparable<androidx.health.connect.client.units.Power> {
     method public int compareTo(androidx.health.connect.client.units.Power other);
     method public double getKilocaloriesPerDay();
diff --git a/health/health-connect-client/api/public_plus_experimental_current.txt b/health/health-connect-client/api/public_plus_experimental_current.txt
index 100f938..a4038e0 100644
--- a/health/health-connect-client/api/public_plus_experimental_current.txt
+++ b/health/health-connect-client/api/public_plus_experimental_current.txt
@@ -230,13 +230,13 @@
   }
 
   public final class BodyFatRecord implements androidx.health.connect.client.records.Record {
-    ctor public BodyFatRecord(int percentage, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public BodyFatRecord(androidx.health.connect.client.units.Percentage percentage, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public int getPercentage();
+    method public androidx.health.connect.client.units.Percentage getPercentage();
     method public java.time.Instant getTime();
     method public java.time.ZoneOffset? getZoneOffset();
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public final int percentage;
+    property public final androidx.health.connect.client.units.Percentage percentage;
     property public java.time.Instant time;
     property public java.time.ZoneOffset? zoneOffset;
   }
@@ -910,13 +910,13 @@
   }
 
   public final class OxygenSaturationRecord implements androidx.health.connect.client.records.Record {
-    ctor public OxygenSaturationRecord(int percentage, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public OxygenSaturationRecord(androidx.health.connect.client.units.Percentage percentage, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public int getPercentage();
+    method public androidx.health.connect.client.units.Percentage getPercentage();
     method public java.time.Instant getTime();
     method public java.time.ZoneOffset? getZoneOffset();
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public final int percentage;
+    property public final androidx.health.connect.client.units.Percentage percentage;
     property public java.time.Instant time;
     property public java.time.ZoneOffset? zoneOffset;
   }
@@ -1493,6 +1493,16 @@
   public final class MassKt {
   }
 
+  public final class Percentage implements java.lang.Comparable<androidx.health.connect.client.units.Percentage> {
+    ctor public Percentage(double value);
+    method public int compareTo(androidx.health.connect.client.units.Percentage other);
+    method public double getValue();
+    property public final double value;
+  }
+
+  public final class PercentageKt {
+  }
+
   public final class Power implements java.lang.Comparable<androidx.health.connect.client.units.Power> {
     method public int compareTo(androidx.health.connect.client.units.Power other);
     method public double getKilocaloriesPerDay();
diff --git a/health/health-connect-client/api/restricted_current.txt b/health/health-connect-client/api/restricted_current.txt
index d597095..c3912ad 100644
--- a/health/health-connect-client/api/restricted_current.txt
+++ b/health/health-connect-client/api/restricted_current.txt
@@ -230,13 +230,13 @@
   }
 
   public final class BodyFatRecord implements androidx.health.connect.client.records.InstantaneousRecord {
-    ctor public BodyFatRecord(int percentage, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public BodyFatRecord(androidx.health.connect.client.units.Percentage percentage, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public int getPercentage();
+    method public androidx.health.connect.client.units.Percentage getPercentage();
     method public java.time.Instant getTime();
     method public java.time.ZoneOffset? getZoneOffset();
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public final int percentage;
+    property public final androidx.health.connect.client.units.Percentage percentage;
     property public java.time.Instant time;
     property public java.time.ZoneOffset? zoneOffset;
   }
@@ -928,13 +928,13 @@
   }
 
   public final class OxygenSaturationRecord implements androidx.health.connect.client.records.InstantaneousRecord {
-    ctor public OxygenSaturationRecord(int percentage, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
+    ctor public OxygenSaturationRecord(androidx.health.connect.client.units.Percentage percentage, java.time.Instant time, java.time.ZoneOffset? zoneOffset, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
-    method public int getPercentage();
+    method public androidx.health.connect.client.units.Percentage getPercentage();
     method public java.time.Instant getTime();
     method public java.time.ZoneOffset? getZoneOffset();
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
-    property public final int percentage;
+    property public final androidx.health.connect.client.units.Percentage percentage;
     property public java.time.Instant time;
     property public java.time.ZoneOffset? zoneOffset;
   }
@@ -1516,6 +1516,16 @@
   public final class MassKt {
   }
 
+  public final class Percentage implements java.lang.Comparable<androidx.health.connect.client.units.Percentage> {
+    ctor public Percentage(double value);
+    method public int compareTo(androidx.health.connect.client.units.Percentage other);
+    method public double getValue();
+    property public final double value;
+  }
+
+  public final class PercentageKt {
+  }
+
   public final class Power implements java.lang.Comparable<androidx.health.connect.client.units.Power> {
     method public int compareTo(androidx.health.connect.client.units.Power other);
     method public double getKilocaloriesPerDay();
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
index bf6ae6c..aaf2034 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
@@ -82,6 +82,7 @@
 import androidx.health.connect.client.units.liters
 import androidx.health.connect.client.units.meters
 import androidx.health.connect.client.units.millimetersOfMercury
+import androidx.health.connect.client.units.percent
 import androidx.health.connect.client.units.watts
 import androidx.health.platform.client.proto.DataProto
 import java.time.Instant
@@ -127,7 +128,7 @@
                 )
             "BodyFat" ->
                 BodyFatRecord(
-                    percentage = getDouble("percentage").toInt(),
+                    percentage = getDouble("percentage").percent,
                     time = time,
                     zoneOffset = zoneOffset,
                     metadata = metadata
@@ -292,7 +293,7 @@
                 )
             "OxygenSaturation" ->
                 OxygenSaturationRecord(
-                    percentage = getDouble("percentage").toInt(),
+                    percentage = getDouble("percentage").percent,
                     time = time,
                     zoneOffset = zoneOffset,
                     metadata = metadata
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt
index fbe8142..7a76ce7 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt
@@ -112,7 +112,7 @@
         is BodyFatRecord ->
             instantaneousProto()
                 .setDataType(protoDataType("BodyFat"))
-                .apply { putValues("percentage", doubleVal(percentage.toDouble())) }
+                .apply { putValues("percentage", doubleVal(percentage.value)) }
                 .build()
         is BodyTemperatureRecord ->
             instantaneousProto()
@@ -227,7 +227,7 @@
         is OxygenSaturationRecord ->
             instantaneousProto()
                 .setDataType(protoDataType("OxygenSaturation"))
-                .apply { putValues("percentage", doubleVal(percentage.toDouble())) }
+                .apply { putValues("percentage", doubleVal(percentage.value)) }
                 .build()
         is PowerRecord ->
             toProto(dataTypeName = "PowerSeries") { sample ->
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/BodyFatRecord.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/BodyFatRecord.kt
index 458fa52..7f51c1b 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/BodyFatRecord.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/BodyFatRecord.kt
@@ -16,6 +16,7 @@
 package androidx.health.connect.client.records
 
 import androidx.health.connect.client.records.metadata.Metadata
+import androidx.health.connect.client.units.Percentage
 import java.time.Instant
 import java.time.ZoneOffset
 
@@ -25,16 +26,19 @@
  */
 public class BodyFatRecord(
     /** Percentage. Required field. Valid range: 0-100. */
-    public val percentage: Int,
+    public val percentage: Percentage,
     override val time: Instant,
     override val zoneOffset: ZoneOffset?,
     override val metadata: Metadata = Metadata.EMPTY,
 ) : InstantaneousRecord {
 
     init {
-        requireNonNegative(value = percentage, name = "percentage")
+        requireNonNegative(value = percentage.value, name = "percentage")
     }
 
+    /*
+     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+     */
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is BodyFatRecord) return false
@@ -47,9 +51,11 @@
         return true
     }
 
+    /*
+     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+     */
     override fun hashCode(): Int {
-        var result = 0
-        result = 31 * result + percentage.hashCode()
+        var result = percentage.hashCode()
         result = 31 * result + time.hashCode()
         result = 31 * result + (zoneOffset?.hashCode() ?: 0)
         result = 31 * result + metadata.hashCode()
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/OxygenSaturationRecord.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/OxygenSaturationRecord.kt
index 3f4b3f8..4c9c0f6 100644
--- a/health/health-connect-client/src/main/java/androidx/health/connect/client/records/OxygenSaturationRecord.kt
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/records/OxygenSaturationRecord.kt
@@ -16,6 +16,7 @@
 package androidx.health.connect.client.records
 
 import androidx.health.connect.client.records.metadata.Metadata
+import androidx.health.connect.client.units.Percentage
 import java.time.Instant
 import java.time.ZoneOffset
 
@@ -26,16 +27,19 @@
  */
 public class OxygenSaturationRecord(
     /** Percentage. Required field. Valid range: 0-100. */
-    public val percentage: Int,
+    public val percentage: Percentage,
     override val time: Instant,
     override val zoneOffset: ZoneOffset?,
     override val metadata: Metadata = Metadata.EMPTY,
 ) : InstantaneousRecord {
 
     init {
-        requireNonNegative(value = percentage, name = "percentage")
+        requireNonNegative(value = percentage.value, name = "percentage")
     }
 
+    /*
+     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+     */
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is OxygenSaturationRecord) return false
@@ -48,9 +52,11 @@
         return true
     }
 
+    /*
+     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+     */
     override fun hashCode(): Int {
-        var result = 0
-        result = 31 * result + percentage.hashCode()
+        var result = percentage.hashCode()
         result = 31 * result + time.hashCode()
         result = 31 * result + (zoneOffset?.hashCode() ?: 0)
         result = 31 * result + metadata.hashCode()
diff --git a/health/health-connect-client/src/main/java/androidx/health/connect/client/units/Percentage.kt b/health/health-connect-client/src/main/java/androidx/health/connect/client/units/Percentage.kt
new file mode 100644
index 0000000..a3a0aae
--- /dev/null
+++ b/health/health-connect-client/src/main/java/androidx/health/connect/client/units/Percentage.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2022 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.health.connect.client.units
+
+/** Represents a percentage value. */
+class Percentage(val value: Double) : Comparable<Percentage> {
+
+    override fun compareTo(other: Percentage): Int = value.compareTo(other.value)
+
+    /*
+     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+     */
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is Percentage) return false
+
+        if (value != other.value) return false
+
+        return true
+    }
+
+    /*
+     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
+     */
+    override fun hashCode(): Int {
+        return value.hashCode()
+    }
+
+    override fun toString(): String = "$value%"
+}
+
+/** Creates [Percentage] with the specified value. */
+@get:JvmSynthetic
+val Double.percent: Percentage
+    get() = Percentage(value = this)
+
+/** Creates [Percentage] with the specified value. */
+@get:JvmSynthetic
+val Long.percent: Percentage
+    get() = toDouble().percent
+
+/** Creates [Percentage] with the specified value. */
+@get:JvmSynthetic
+val Float.percent: Percentage
+    get() = toDouble().percent
+
+/** Creates [Percentage] with the specified value. */
+@get:JvmSynthetic
+val Int.percent: Percentage
+    get() = toDouble().percent
diff --git a/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt b/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
index ea11ca7..4e9203a 100644
--- a/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
+++ b/health/health-connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
@@ -92,6 +92,7 @@
 import androidx.health.connect.client.units.liters
 import androidx.health.connect.client.units.meters
 import androidx.health.connect.client.units.millimetersOfMercury
+import androidx.health.connect.client.units.percent
 import androidx.health.connect.client.units.watts
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
@@ -198,7 +199,7 @@
     fun testBodyFat() {
         val data =
             BodyFatRecord(
-                percentage = 1,
+                percentage = 1.percent,
                 time = START_TIME,
                 zoneOffset = END_ZONE_OFFSET,
                 metadata = TEST_METADATA
@@ -522,7 +523,7 @@
     fun testOxygenSaturation() {
         val data =
             OxygenSaturationRecord(
-                percentage = 1,
+                percentage = 1.percent,
                 time = START_TIME,
                 zoneOffset = END_ZONE_OFFSET,
                 metadata = TEST_METADATA
diff --git a/leanback/leanback/api/current.txt b/leanback/leanback/api/current.txt
index 2c2317e..d38fb99 100644
--- a/leanback/leanback/api/current.txt
+++ b/leanback/leanback/api/current.txt
@@ -952,13 +952,13 @@
 
   public class VerticalGridSupportFragment extends androidx.leanback.app.BaseSupportFragment {
     ctor public VerticalGridSupportFragment();
-    method public androidx.leanback.widget.ObjectAdapter! getAdapter();
-    method public androidx.leanback.widget.VerticalGridPresenter! getGridPresenter();
-    method public androidx.leanback.widget.OnItemViewClickedListener! getOnItemViewClickedListener();
-    method public void setAdapter(androidx.leanback.widget.ObjectAdapter!);
-    method public void setGridPresenter(androidx.leanback.widget.VerticalGridPresenter!);
-    method public void setOnItemViewClickedListener(androidx.leanback.widget.OnItemViewClickedListener!);
-    method public void setOnItemViewSelectedListener(androidx.leanback.widget.OnItemViewSelectedListener!);
+    method public androidx.leanback.widget.ObjectAdapter? getAdapter();
+    method public androidx.leanback.widget.VerticalGridPresenter? getGridPresenter();
+    method public androidx.leanback.widget.OnItemViewClickedListener? getOnItemViewClickedListener();
+    method public void setAdapter(androidx.leanback.widget.ObjectAdapter?);
+    method public void setGridPresenter(androidx.leanback.widget.VerticalGridPresenter);
+    method public void setOnItemViewClickedListener(androidx.leanback.widget.OnItemViewClickedListener?);
+    method public void setOnItemViewSelectedListener(androidx.leanback.widget.OnItemViewSelectedListener?);
     method public void setSelectedPosition(int);
   }
 
@@ -1047,26 +1047,26 @@
 
   public class CompositeDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
     ctor public CompositeDrawable();
-    method public void addChildDrawable(android.graphics.drawable.Drawable!);
-    method public void draw(android.graphics.Canvas!);
-    method public androidx.leanback.graphics.CompositeDrawable.ChildDrawable! getChildAt(int);
+    method public void addChildDrawable(android.graphics.drawable.Drawable);
+    method public void draw(android.graphics.Canvas);
+    method public androidx.leanback.graphics.CompositeDrawable.ChildDrawable getChildAt(int);
     method public int getChildCount();
-    method public android.graphics.drawable.Drawable! getDrawable(int);
+    method public android.graphics.drawable.Drawable getDrawable(int);
     method public int getOpacity();
-    method public void invalidateDrawable(android.graphics.drawable.Drawable!);
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
     method public void removeChild(int);
-    method public void removeDrawable(android.graphics.drawable.Drawable!);
-    method public void scheduleDrawable(android.graphics.drawable.Drawable!, Runnable!, long);
+    method public void removeDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long);
     method public void setAlpha(int);
-    method public void setChildDrawableAt(int, android.graphics.drawable.Drawable!);
-    method public void setColorFilter(android.graphics.ColorFilter!);
-    method public void unscheduleDrawable(android.graphics.drawable.Drawable!, Runnable!);
+    method public void setChildDrawableAt(int, android.graphics.drawable.Drawable);
+    method public void setColorFilter(android.graphics.ColorFilter?);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, Runnable);
   }
 
   public static final class CompositeDrawable.ChildDrawable {
-    ctor public CompositeDrawable.ChildDrawable(android.graphics.drawable.Drawable!, androidx.leanback.graphics.CompositeDrawable!);
-    method public androidx.leanback.graphics.BoundsRule! getBoundsRule();
-    method public android.graphics.drawable.Drawable! getDrawable();
+    ctor public CompositeDrawable.ChildDrawable(android.graphics.drawable.Drawable, androidx.leanback.graphics.CompositeDrawable);
+    method public androidx.leanback.graphics.BoundsRule getBoundsRule();
+    method public android.graphics.drawable.Drawable getDrawable();
     method public void recomputeBounds();
     field public static final android.util.Property<androidx.leanback.graphics.CompositeDrawable.ChildDrawable!,java.lang.Integer!>! BOTTOM_ABSOLUTE;
     field public static final android.util.Property<androidx.leanback.graphics.CompositeDrawable.ChildDrawable!,java.lang.Float!>! BOTTOM_FRACTION;
@@ -1597,22 +1597,22 @@
 
   public class DetailsOverviewLogoPresenter extends androidx.leanback.widget.Presenter {
     ctor public DetailsOverviewLogoPresenter();
-    method public boolean isBoundToImage(androidx.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder!, androidx.leanback.widget.DetailsOverviewRow!);
-    method public void onBindViewHolder(androidx.leanback.widget.Presenter.ViewHolder!, Object!);
-    method public android.view.View! onCreateView(android.view.ViewGroup!);
-    method public androidx.leanback.widget.Presenter.ViewHolder! onCreateViewHolder(android.view.ViewGroup!);
-    method public void onUnbindViewHolder(androidx.leanback.widget.Presenter.ViewHolder!);
-    method public void setContext(androidx.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder!, androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder!, androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter!);
+    method public boolean isBoundToImage(androidx.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, androidx.leanback.widget.DetailsOverviewRow?);
+    method public void onBindViewHolder(androidx.leanback.widget.Presenter.ViewHolder, Object);
+    method public android.view.View onCreateView(android.view.ViewGroup);
+    method public androidx.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(androidx.leanback.widget.Presenter.ViewHolder);
+    method public void setContext(androidx.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder?, androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter?);
   }
 
   public static class DetailsOverviewLogoPresenter.ViewHolder extends androidx.leanback.widget.Presenter.ViewHolder {
-    ctor public DetailsOverviewLogoPresenter.ViewHolder(android.view.View!);
-    method public androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter! getParentPresenter();
-    method public androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder! getParentViewHolder();
+    ctor public DetailsOverviewLogoPresenter.ViewHolder(android.view.View);
+    method public androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter? getParentPresenter();
+    method public androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder? getParentViewHolder();
     method public boolean isSizeFromDrawableIntrinsic();
     method public void setSizeFromDrawableIntrinsic(boolean);
-    field protected androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter! mParentPresenter;
-    field protected androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder! mParentViewHolder;
+    field protected androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter? mParentPresenter;
+    field protected androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder? mParentViewHolder;
   }
 
   public class DetailsOverviewRow extends androidx.leanback.widget.Row {
@@ -1793,16 +1793,16 @@
     ctor protected GuidedAction();
     method public String![]! getAutofillHints();
     method public int getCheckSetId();
-    method public CharSequence! getDescription();
+    method public CharSequence? getDescription();
     method public int getDescriptionEditInputType();
     method public int getDescriptionInputType();
-    method public CharSequence! getEditDescription();
+    method public CharSequence? getEditDescription();
     method public int getEditInputType();
-    method public CharSequence! getEditTitle();
+    method public CharSequence? getEditTitle();
     method public int getInputType();
-    method public android.content.Intent! getIntent();
-    method public java.util.List<androidx.leanback.widget.GuidedAction!>! getSubActions();
-    method public CharSequence! getTitle();
+    method public android.content.Intent? getIntent();
+    method public java.util.List<androidx.leanback.widget.GuidedAction!>? getSubActions();
+    method public CharSequence? getTitle();
     method public boolean hasEditableActivatorView();
     method public boolean hasMultilineDescription();
     method public boolean hasNext();
@@ -1816,17 +1816,17 @@
     method public boolean isEditable();
     method public boolean isEnabled();
     method public boolean isFocusable();
-    method public void onRestoreInstanceState(android.os.Bundle!, String!);
-    method public void onSaveInstanceState(android.os.Bundle!, String!);
+    method public void onRestoreInstanceState(android.os.Bundle, String);
+    method public void onSaveInstanceState(android.os.Bundle, String);
     method public void setChecked(boolean);
-    method public void setDescription(CharSequence!);
-    method public void setEditDescription(CharSequence!);
-    method public void setEditTitle(CharSequence!);
+    method public void setDescription(CharSequence?);
+    method public void setEditDescription(CharSequence?);
+    method public void setEditTitle(CharSequence?);
     method public void setEnabled(boolean);
     method public void setFocusable(boolean);
-    method public void setIntent(android.content.Intent!);
-    method public void setSubActions(java.util.List<androidx.leanback.widget.GuidedAction!>!);
-    method public void setTitle(CharSequence!);
+    method public void setIntent(android.content.Intent?);
+    method public void setSubActions(java.util.List<androidx.leanback.widget.GuidedAction!>?);
+    method public void setTitle(CharSequence?);
     field public static final long ACTION_ID_CANCEL = -5L; // 0xfffffffffffffffbL
     field public static final long ACTION_ID_CONTINUE = -7L; // 0xfffffffffffffff9L
     field public static final long ACTION_ID_CURRENT = -3L; // 0xfffffffffffffffdL
@@ -1842,44 +1842,44 @@
 
   public static class GuidedAction.Builder extends androidx.leanback.widget.GuidedAction.BuilderBase<androidx.leanback.widget.GuidedAction.Builder> {
     ctor @Deprecated public GuidedAction.Builder();
-    ctor public GuidedAction.Builder(android.content.Context!);
-    method public androidx.leanback.widget.GuidedAction! build();
+    ctor public GuidedAction.Builder(android.content.Context?);
+    method public androidx.leanback.widget.GuidedAction build();
   }
 
   public abstract static class GuidedAction.BuilderBase<B extends androidx.leanback.widget.GuidedAction.BuilderBase> {
-    ctor public GuidedAction.BuilderBase(android.content.Context!);
-    method protected final void applyValues(androidx.leanback.widget.GuidedAction!);
+    ctor public GuidedAction.BuilderBase(android.content.Context);
+    method protected final void applyValues(androidx.leanback.widget.GuidedAction);
     method public B! autoSaveRestoreEnabled(boolean);
     method public B! autofillHints(java.lang.String!...);
     method public B! checkSetId(int);
     method public B! checked(boolean);
     method public B! clickAction(long);
-    method public B! description(CharSequence!);
+    method public B! description(CharSequence?);
     method public B! description(@StringRes int);
     method public B! descriptionEditInputType(int);
     method public B! descriptionEditable(boolean);
     method public B! descriptionInputType(int);
-    method public B! editDescription(CharSequence!);
+    method public B! editDescription(CharSequence?);
     method public B! editDescription(@StringRes int);
     method public B! editInputType(int);
-    method public B! editTitle(CharSequence!);
+    method public B! editTitle(CharSequence?);
     method public B! editTitle(@StringRes int);
     method public B! editable(boolean);
     method public B! enabled(boolean);
     method public B! focusable(boolean);
-    method public android.content.Context! getContext();
+    method public android.content.Context getContext();
     method public B! hasEditableActivatorView(boolean);
     method public B! hasNext(boolean);
-    method public B! icon(android.graphics.drawable.Drawable!);
+    method public B! icon(android.graphics.drawable.Drawable?);
     method public B! icon(@DrawableRes int);
     method @Deprecated public B! iconResourceId(@DrawableRes int, android.content.Context!);
     method public B! id(long);
     method public B! infoOnly(boolean);
     method public B! inputType(int);
-    method public B! intent(android.content.Intent!);
+    method public B! intent(android.content.Intent?);
     method public B! multilineDescription(boolean);
-    method public B! subActions(java.util.List<androidx.leanback.widget.GuidedAction!>!);
-    method public B! title(CharSequence!);
+    method public B! subActions(java.util.List<androidx.leanback.widget.GuidedAction!>?);
+    method public B! title(CharSequence?);
     method public B! title(@StringRes int);
   }
 
@@ -2764,19 +2764,19 @@
   }
 
   public class SearchOrbView extends android.widget.FrameLayout implements android.view.View.OnClickListener {
-    ctor public SearchOrbView(android.content.Context!);
-    ctor public SearchOrbView(android.content.Context!, android.util.AttributeSet!);
-    ctor public SearchOrbView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public SearchOrbView(android.content.Context);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet?);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet?, int);
     method public void enableOrbColorAnimation(boolean);
     method @ColorInt public int getOrbColor();
-    method public androidx.leanback.widget.SearchOrbView.Colors! getOrbColors();
-    method public android.graphics.drawable.Drawable! getOrbIcon();
+    method public androidx.leanback.widget.SearchOrbView.Colors? getOrbColors();
+    method public android.graphics.drawable.Drawable? getOrbIcon();
     method public void onClick(android.view.View!);
-    method public void setOnOrbClickedListener(android.view.View.OnClickListener!);
+    method public void setOnOrbClickedListener(android.view.View.OnClickListener?);
     method public void setOrbColor(int);
     method @Deprecated public void setOrbColor(@ColorInt int, @ColorInt int);
-    method public void setOrbColors(androidx.leanback.widget.SearchOrbView.Colors!);
-    method public void setOrbIcon(android.graphics.drawable.Drawable!);
+    method public void setOrbColors(androidx.leanback.widget.SearchOrbView.Colors);
+    method public void setOrbIcon(android.graphics.drawable.Drawable);
   }
 
   public static class SearchOrbView.Colors {
@@ -2904,19 +2904,19 @@
   }
 
   public class TitleView extends android.widget.FrameLayout implements androidx.leanback.widget.TitleViewAdapter.Provider {
-    ctor public TitleView(android.content.Context!);
-    ctor public TitleView(android.content.Context!, android.util.AttributeSet!);
-    ctor public TitleView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public TitleView(android.content.Context);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet?);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet?, int);
     method public void enableAnimation(boolean);
-    method public android.graphics.drawable.Drawable! getBadgeDrawable();
-    method public androidx.leanback.widget.SearchOrbView.Colors! getSearchAffordanceColors();
-    method public android.view.View! getSearchAffordanceView();
-    method public CharSequence! getTitle();
-    method public androidx.leanback.widget.TitleViewAdapter! getTitleViewAdapter();
-    method public void setBadgeDrawable(android.graphics.drawable.Drawable!);
-    method public void setOnSearchClickedListener(android.view.View.OnClickListener!);
-    method public void setSearchAffordanceColors(androidx.leanback.widget.SearchOrbView.Colors!);
-    method public void setTitle(CharSequence!);
+    method public android.graphics.drawable.Drawable? getBadgeDrawable();
+    method public androidx.leanback.widget.SearchOrbView.Colors? getSearchAffordanceColors();
+    method public android.view.View getSearchAffordanceView();
+    method public CharSequence? getTitle();
+    method public androidx.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable?);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener?);
+    method public void setSearchAffordanceColors(androidx.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(CharSequence?);
     method public void updateComponentsVisibility(int);
   }
 
diff --git a/leanback/leanback/api/public_plus_experimental_current.txt b/leanback/leanback/api/public_plus_experimental_current.txt
index 2c2317e..d38fb99 100644
--- a/leanback/leanback/api/public_plus_experimental_current.txt
+++ b/leanback/leanback/api/public_plus_experimental_current.txt
@@ -952,13 +952,13 @@
 
   public class VerticalGridSupportFragment extends androidx.leanback.app.BaseSupportFragment {
     ctor public VerticalGridSupportFragment();
-    method public androidx.leanback.widget.ObjectAdapter! getAdapter();
-    method public androidx.leanback.widget.VerticalGridPresenter! getGridPresenter();
-    method public androidx.leanback.widget.OnItemViewClickedListener! getOnItemViewClickedListener();
-    method public void setAdapter(androidx.leanback.widget.ObjectAdapter!);
-    method public void setGridPresenter(androidx.leanback.widget.VerticalGridPresenter!);
-    method public void setOnItemViewClickedListener(androidx.leanback.widget.OnItemViewClickedListener!);
-    method public void setOnItemViewSelectedListener(androidx.leanback.widget.OnItemViewSelectedListener!);
+    method public androidx.leanback.widget.ObjectAdapter? getAdapter();
+    method public androidx.leanback.widget.VerticalGridPresenter? getGridPresenter();
+    method public androidx.leanback.widget.OnItemViewClickedListener? getOnItemViewClickedListener();
+    method public void setAdapter(androidx.leanback.widget.ObjectAdapter?);
+    method public void setGridPresenter(androidx.leanback.widget.VerticalGridPresenter);
+    method public void setOnItemViewClickedListener(androidx.leanback.widget.OnItemViewClickedListener?);
+    method public void setOnItemViewSelectedListener(androidx.leanback.widget.OnItemViewSelectedListener?);
     method public void setSelectedPosition(int);
   }
 
@@ -1047,26 +1047,26 @@
 
   public class CompositeDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
     ctor public CompositeDrawable();
-    method public void addChildDrawable(android.graphics.drawable.Drawable!);
-    method public void draw(android.graphics.Canvas!);
-    method public androidx.leanback.graphics.CompositeDrawable.ChildDrawable! getChildAt(int);
+    method public void addChildDrawable(android.graphics.drawable.Drawable);
+    method public void draw(android.graphics.Canvas);
+    method public androidx.leanback.graphics.CompositeDrawable.ChildDrawable getChildAt(int);
     method public int getChildCount();
-    method public android.graphics.drawable.Drawable! getDrawable(int);
+    method public android.graphics.drawable.Drawable getDrawable(int);
     method public int getOpacity();
-    method public void invalidateDrawable(android.graphics.drawable.Drawable!);
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
     method public void removeChild(int);
-    method public void removeDrawable(android.graphics.drawable.Drawable!);
-    method public void scheduleDrawable(android.graphics.drawable.Drawable!, Runnable!, long);
+    method public void removeDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long);
     method public void setAlpha(int);
-    method public void setChildDrawableAt(int, android.graphics.drawable.Drawable!);
-    method public void setColorFilter(android.graphics.ColorFilter!);
-    method public void unscheduleDrawable(android.graphics.drawable.Drawable!, Runnable!);
+    method public void setChildDrawableAt(int, android.graphics.drawable.Drawable);
+    method public void setColorFilter(android.graphics.ColorFilter?);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, Runnable);
   }
 
   public static final class CompositeDrawable.ChildDrawable {
-    ctor public CompositeDrawable.ChildDrawable(android.graphics.drawable.Drawable!, androidx.leanback.graphics.CompositeDrawable!);
-    method public androidx.leanback.graphics.BoundsRule! getBoundsRule();
-    method public android.graphics.drawable.Drawable! getDrawable();
+    ctor public CompositeDrawable.ChildDrawable(android.graphics.drawable.Drawable, androidx.leanback.graphics.CompositeDrawable);
+    method public androidx.leanback.graphics.BoundsRule getBoundsRule();
+    method public android.graphics.drawable.Drawable getDrawable();
     method public void recomputeBounds();
     field public static final android.util.Property<androidx.leanback.graphics.CompositeDrawable.ChildDrawable!,java.lang.Integer!>! BOTTOM_ABSOLUTE;
     field public static final android.util.Property<androidx.leanback.graphics.CompositeDrawable.ChildDrawable!,java.lang.Float!>! BOTTOM_FRACTION;
@@ -1597,22 +1597,22 @@
 
   public class DetailsOverviewLogoPresenter extends androidx.leanback.widget.Presenter {
     ctor public DetailsOverviewLogoPresenter();
-    method public boolean isBoundToImage(androidx.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder!, androidx.leanback.widget.DetailsOverviewRow!);
-    method public void onBindViewHolder(androidx.leanback.widget.Presenter.ViewHolder!, Object!);
-    method public android.view.View! onCreateView(android.view.ViewGroup!);
-    method public androidx.leanback.widget.Presenter.ViewHolder! onCreateViewHolder(android.view.ViewGroup!);
-    method public void onUnbindViewHolder(androidx.leanback.widget.Presenter.ViewHolder!);
-    method public void setContext(androidx.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder!, androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder!, androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter!);
+    method public boolean isBoundToImage(androidx.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, androidx.leanback.widget.DetailsOverviewRow?);
+    method public void onBindViewHolder(androidx.leanback.widget.Presenter.ViewHolder, Object);
+    method public android.view.View onCreateView(android.view.ViewGroup);
+    method public androidx.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(androidx.leanback.widget.Presenter.ViewHolder);
+    method public void setContext(androidx.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder?, androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter?);
   }
 
   public static class DetailsOverviewLogoPresenter.ViewHolder extends androidx.leanback.widget.Presenter.ViewHolder {
-    ctor public DetailsOverviewLogoPresenter.ViewHolder(android.view.View!);
-    method public androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter! getParentPresenter();
-    method public androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder! getParentViewHolder();
+    ctor public DetailsOverviewLogoPresenter.ViewHolder(android.view.View);
+    method public androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter? getParentPresenter();
+    method public androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder? getParentViewHolder();
     method public boolean isSizeFromDrawableIntrinsic();
     method public void setSizeFromDrawableIntrinsic(boolean);
-    field protected androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter! mParentPresenter;
-    field protected androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder! mParentViewHolder;
+    field protected androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter? mParentPresenter;
+    field protected androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder? mParentViewHolder;
   }
 
   public class DetailsOverviewRow extends androidx.leanback.widget.Row {
@@ -1793,16 +1793,16 @@
     ctor protected GuidedAction();
     method public String![]! getAutofillHints();
     method public int getCheckSetId();
-    method public CharSequence! getDescription();
+    method public CharSequence? getDescription();
     method public int getDescriptionEditInputType();
     method public int getDescriptionInputType();
-    method public CharSequence! getEditDescription();
+    method public CharSequence? getEditDescription();
     method public int getEditInputType();
-    method public CharSequence! getEditTitle();
+    method public CharSequence? getEditTitle();
     method public int getInputType();
-    method public android.content.Intent! getIntent();
-    method public java.util.List<androidx.leanback.widget.GuidedAction!>! getSubActions();
-    method public CharSequence! getTitle();
+    method public android.content.Intent? getIntent();
+    method public java.util.List<androidx.leanback.widget.GuidedAction!>? getSubActions();
+    method public CharSequence? getTitle();
     method public boolean hasEditableActivatorView();
     method public boolean hasMultilineDescription();
     method public boolean hasNext();
@@ -1816,17 +1816,17 @@
     method public boolean isEditable();
     method public boolean isEnabled();
     method public boolean isFocusable();
-    method public void onRestoreInstanceState(android.os.Bundle!, String!);
-    method public void onSaveInstanceState(android.os.Bundle!, String!);
+    method public void onRestoreInstanceState(android.os.Bundle, String);
+    method public void onSaveInstanceState(android.os.Bundle, String);
     method public void setChecked(boolean);
-    method public void setDescription(CharSequence!);
-    method public void setEditDescription(CharSequence!);
-    method public void setEditTitle(CharSequence!);
+    method public void setDescription(CharSequence?);
+    method public void setEditDescription(CharSequence?);
+    method public void setEditTitle(CharSequence?);
     method public void setEnabled(boolean);
     method public void setFocusable(boolean);
-    method public void setIntent(android.content.Intent!);
-    method public void setSubActions(java.util.List<androidx.leanback.widget.GuidedAction!>!);
-    method public void setTitle(CharSequence!);
+    method public void setIntent(android.content.Intent?);
+    method public void setSubActions(java.util.List<androidx.leanback.widget.GuidedAction!>?);
+    method public void setTitle(CharSequence?);
     field public static final long ACTION_ID_CANCEL = -5L; // 0xfffffffffffffffbL
     field public static final long ACTION_ID_CONTINUE = -7L; // 0xfffffffffffffff9L
     field public static final long ACTION_ID_CURRENT = -3L; // 0xfffffffffffffffdL
@@ -1842,44 +1842,44 @@
 
   public static class GuidedAction.Builder extends androidx.leanback.widget.GuidedAction.BuilderBase<androidx.leanback.widget.GuidedAction.Builder> {
     ctor @Deprecated public GuidedAction.Builder();
-    ctor public GuidedAction.Builder(android.content.Context!);
-    method public androidx.leanback.widget.GuidedAction! build();
+    ctor public GuidedAction.Builder(android.content.Context?);
+    method public androidx.leanback.widget.GuidedAction build();
   }
 
   public abstract static class GuidedAction.BuilderBase<B extends androidx.leanback.widget.GuidedAction.BuilderBase> {
-    ctor public GuidedAction.BuilderBase(android.content.Context!);
-    method protected final void applyValues(androidx.leanback.widget.GuidedAction!);
+    ctor public GuidedAction.BuilderBase(android.content.Context);
+    method protected final void applyValues(androidx.leanback.widget.GuidedAction);
     method public B! autoSaveRestoreEnabled(boolean);
     method public B! autofillHints(java.lang.String!...);
     method public B! checkSetId(int);
     method public B! checked(boolean);
     method public B! clickAction(long);
-    method public B! description(CharSequence!);
+    method public B! description(CharSequence?);
     method public B! description(@StringRes int);
     method public B! descriptionEditInputType(int);
     method public B! descriptionEditable(boolean);
     method public B! descriptionInputType(int);
-    method public B! editDescription(CharSequence!);
+    method public B! editDescription(CharSequence?);
     method public B! editDescription(@StringRes int);
     method public B! editInputType(int);
-    method public B! editTitle(CharSequence!);
+    method public B! editTitle(CharSequence?);
     method public B! editTitle(@StringRes int);
     method public B! editable(boolean);
     method public B! enabled(boolean);
     method public B! focusable(boolean);
-    method public android.content.Context! getContext();
+    method public android.content.Context getContext();
     method public B! hasEditableActivatorView(boolean);
     method public B! hasNext(boolean);
-    method public B! icon(android.graphics.drawable.Drawable!);
+    method public B! icon(android.graphics.drawable.Drawable?);
     method public B! icon(@DrawableRes int);
     method @Deprecated public B! iconResourceId(@DrawableRes int, android.content.Context!);
     method public B! id(long);
     method public B! infoOnly(boolean);
     method public B! inputType(int);
-    method public B! intent(android.content.Intent!);
+    method public B! intent(android.content.Intent?);
     method public B! multilineDescription(boolean);
-    method public B! subActions(java.util.List<androidx.leanback.widget.GuidedAction!>!);
-    method public B! title(CharSequence!);
+    method public B! subActions(java.util.List<androidx.leanback.widget.GuidedAction!>?);
+    method public B! title(CharSequence?);
     method public B! title(@StringRes int);
   }
 
@@ -2764,19 +2764,19 @@
   }
 
   public class SearchOrbView extends android.widget.FrameLayout implements android.view.View.OnClickListener {
-    ctor public SearchOrbView(android.content.Context!);
-    ctor public SearchOrbView(android.content.Context!, android.util.AttributeSet!);
-    ctor public SearchOrbView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public SearchOrbView(android.content.Context);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet?);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet?, int);
     method public void enableOrbColorAnimation(boolean);
     method @ColorInt public int getOrbColor();
-    method public androidx.leanback.widget.SearchOrbView.Colors! getOrbColors();
-    method public android.graphics.drawable.Drawable! getOrbIcon();
+    method public androidx.leanback.widget.SearchOrbView.Colors? getOrbColors();
+    method public android.graphics.drawable.Drawable? getOrbIcon();
     method public void onClick(android.view.View!);
-    method public void setOnOrbClickedListener(android.view.View.OnClickListener!);
+    method public void setOnOrbClickedListener(android.view.View.OnClickListener?);
     method public void setOrbColor(int);
     method @Deprecated public void setOrbColor(@ColorInt int, @ColorInt int);
-    method public void setOrbColors(androidx.leanback.widget.SearchOrbView.Colors!);
-    method public void setOrbIcon(android.graphics.drawable.Drawable!);
+    method public void setOrbColors(androidx.leanback.widget.SearchOrbView.Colors);
+    method public void setOrbIcon(android.graphics.drawable.Drawable);
   }
 
   public static class SearchOrbView.Colors {
@@ -2904,19 +2904,19 @@
   }
 
   public class TitleView extends android.widget.FrameLayout implements androidx.leanback.widget.TitleViewAdapter.Provider {
-    ctor public TitleView(android.content.Context!);
-    ctor public TitleView(android.content.Context!, android.util.AttributeSet!);
-    ctor public TitleView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public TitleView(android.content.Context);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet?);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet?, int);
     method public void enableAnimation(boolean);
-    method public android.graphics.drawable.Drawable! getBadgeDrawable();
-    method public androidx.leanback.widget.SearchOrbView.Colors! getSearchAffordanceColors();
-    method public android.view.View! getSearchAffordanceView();
-    method public CharSequence! getTitle();
-    method public androidx.leanback.widget.TitleViewAdapter! getTitleViewAdapter();
-    method public void setBadgeDrawable(android.graphics.drawable.Drawable!);
-    method public void setOnSearchClickedListener(android.view.View.OnClickListener!);
-    method public void setSearchAffordanceColors(androidx.leanback.widget.SearchOrbView.Colors!);
-    method public void setTitle(CharSequence!);
+    method public android.graphics.drawable.Drawable? getBadgeDrawable();
+    method public androidx.leanback.widget.SearchOrbView.Colors? getSearchAffordanceColors();
+    method public android.view.View getSearchAffordanceView();
+    method public CharSequence? getTitle();
+    method public androidx.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable?);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener?);
+    method public void setSearchAffordanceColors(androidx.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(CharSequence?);
     method public void updateComponentsVisibility(int);
   }
 
diff --git a/leanback/leanback/api/restricted_current.txt b/leanback/leanback/api/restricted_current.txt
index 0424206..6aec3b7 100644
--- a/leanback/leanback/api/restricted_current.txt
+++ b/leanback/leanback/api/restricted_current.txt
@@ -993,13 +993,13 @@
 
   public class VerticalGridSupportFragment extends androidx.leanback.app.BaseSupportFragment {
     ctor public VerticalGridSupportFragment();
-    method public androidx.leanback.widget.ObjectAdapter! getAdapter();
-    method public androidx.leanback.widget.VerticalGridPresenter! getGridPresenter();
-    method public androidx.leanback.widget.OnItemViewClickedListener! getOnItemViewClickedListener();
-    method public void setAdapter(androidx.leanback.widget.ObjectAdapter!);
-    method public void setGridPresenter(androidx.leanback.widget.VerticalGridPresenter!);
-    method public void setOnItemViewClickedListener(androidx.leanback.widget.OnItemViewClickedListener!);
-    method public void setOnItemViewSelectedListener(androidx.leanback.widget.OnItemViewSelectedListener!);
+    method public androidx.leanback.widget.ObjectAdapter? getAdapter();
+    method public androidx.leanback.widget.VerticalGridPresenter? getGridPresenter();
+    method public androidx.leanback.widget.OnItemViewClickedListener? getOnItemViewClickedListener();
+    method public void setAdapter(androidx.leanback.widget.ObjectAdapter?);
+    method public void setGridPresenter(androidx.leanback.widget.VerticalGridPresenter);
+    method public void setOnItemViewClickedListener(androidx.leanback.widget.OnItemViewClickedListener?);
+    method public void setOnItemViewSelectedListener(androidx.leanback.widget.OnItemViewSelectedListener?);
     method public void setSelectedPosition(int);
   }
 
@@ -1088,26 +1088,26 @@
 
   public class CompositeDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
     ctor public CompositeDrawable();
-    method public void addChildDrawable(android.graphics.drawable.Drawable!);
-    method public void draw(android.graphics.Canvas!);
-    method public androidx.leanback.graphics.CompositeDrawable.ChildDrawable! getChildAt(int);
+    method public void addChildDrawable(android.graphics.drawable.Drawable);
+    method public void draw(android.graphics.Canvas);
+    method public androidx.leanback.graphics.CompositeDrawable.ChildDrawable getChildAt(int);
     method public int getChildCount();
-    method public android.graphics.drawable.Drawable! getDrawable(int);
+    method public android.graphics.drawable.Drawable getDrawable(int);
     method public int getOpacity();
-    method public void invalidateDrawable(android.graphics.drawable.Drawable!);
+    method public void invalidateDrawable(android.graphics.drawable.Drawable);
     method public void removeChild(int);
-    method public void removeDrawable(android.graphics.drawable.Drawable!);
-    method public void scheduleDrawable(android.graphics.drawable.Drawable!, Runnable!, long);
+    method public void removeDrawable(android.graphics.drawable.Drawable);
+    method public void scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long);
     method public void setAlpha(int);
-    method public void setChildDrawableAt(int, android.graphics.drawable.Drawable!);
-    method public void setColorFilter(android.graphics.ColorFilter!);
-    method public void unscheduleDrawable(android.graphics.drawable.Drawable!, Runnable!);
+    method public void setChildDrawableAt(int, android.graphics.drawable.Drawable);
+    method public void setColorFilter(android.graphics.ColorFilter?);
+    method public void unscheduleDrawable(android.graphics.drawable.Drawable, Runnable);
   }
 
   public static final class CompositeDrawable.ChildDrawable {
-    ctor public CompositeDrawable.ChildDrawable(android.graphics.drawable.Drawable!, androidx.leanback.graphics.CompositeDrawable!);
-    method public androidx.leanback.graphics.BoundsRule! getBoundsRule();
-    method public android.graphics.drawable.Drawable! getDrawable();
+    ctor public CompositeDrawable.ChildDrawable(android.graphics.drawable.Drawable, androidx.leanback.graphics.CompositeDrawable);
+    method public androidx.leanback.graphics.BoundsRule getBoundsRule();
+    method public android.graphics.drawable.Drawable getDrawable();
     method public void recomputeBounds();
     field public static final android.util.Property<androidx.leanback.graphics.CompositeDrawable.ChildDrawable!,java.lang.Integer!>! BOTTOM_ABSOLUTE;
     field public static final android.util.Property<androidx.leanback.graphics.CompositeDrawable.ChildDrawable!,java.lang.Float!>! BOTTOM_FRACTION;
@@ -1758,22 +1758,22 @@
 
   public class DetailsOverviewLogoPresenter extends androidx.leanback.widget.Presenter {
     ctor public DetailsOverviewLogoPresenter();
-    method public boolean isBoundToImage(androidx.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder!, androidx.leanback.widget.DetailsOverviewRow!);
-    method public void onBindViewHolder(androidx.leanback.widget.Presenter.ViewHolder!, Object!);
-    method public android.view.View! onCreateView(android.view.ViewGroup!);
-    method public androidx.leanback.widget.Presenter.ViewHolder! onCreateViewHolder(android.view.ViewGroup!);
-    method public void onUnbindViewHolder(androidx.leanback.widget.Presenter.ViewHolder!);
-    method public void setContext(androidx.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder!, androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder!, androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter!);
+    method public boolean isBoundToImage(androidx.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, androidx.leanback.widget.DetailsOverviewRow?);
+    method public void onBindViewHolder(androidx.leanback.widget.Presenter.ViewHolder, Object);
+    method public android.view.View onCreateView(android.view.ViewGroup);
+    method public androidx.leanback.widget.Presenter.ViewHolder onCreateViewHolder(android.view.ViewGroup);
+    method public void onUnbindViewHolder(androidx.leanback.widget.Presenter.ViewHolder);
+    method public void setContext(androidx.leanback.widget.DetailsOverviewLogoPresenter.ViewHolder, androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder?, androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter?);
   }
 
   public static class DetailsOverviewLogoPresenter.ViewHolder extends androidx.leanback.widget.Presenter.ViewHolder {
-    ctor public DetailsOverviewLogoPresenter.ViewHolder(android.view.View!);
-    method public androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter! getParentPresenter();
-    method public androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder! getParentViewHolder();
+    ctor public DetailsOverviewLogoPresenter.ViewHolder(android.view.View);
+    method public androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter? getParentPresenter();
+    method public androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder? getParentViewHolder();
     method public boolean isSizeFromDrawableIntrinsic();
     method public void setSizeFromDrawableIntrinsic(boolean);
-    field protected androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter! mParentPresenter;
-    field protected androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder! mParentViewHolder;
+    field protected androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter? mParentPresenter;
+    field protected androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder? mParentViewHolder;
   }
 
   public class DetailsOverviewRow extends androidx.leanback.widget.Row {
@@ -1955,16 +1955,16 @@
     ctor protected GuidedAction();
     method public String![]! getAutofillHints();
     method public int getCheckSetId();
-    method public CharSequence! getDescription();
+    method public CharSequence? getDescription();
     method public int getDescriptionEditInputType();
     method public int getDescriptionInputType();
-    method public CharSequence! getEditDescription();
+    method public CharSequence? getEditDescription();
     method public int getEditInputType();
-    method public CharSequence! getEditTitle();
+    method public CharSequence? getEditTitle();
     method public int getInputType();
-    method public android.content.Intent! getIntent();
-    method public java.util.List<androidx.leanback.widget.GuidedAction!>! getSubActions();
-    method public CharSequence! getTitle();
+    method public android.content.Intent? getIntent();
+    method public java.util.List<androidx.leanback.widget.GuidedAction!>? getSubActions();
+    method public CharSequence? getTitle();
     method public boolean hasEditableActivatorView();
     method public boolean hasMultilineDescription();
     method public boolean hasNext();
@@ -1978,17 +1978,17 @@
     method public boolean isEditable();
     method public boolean isEnabled();
     method public boolean isFocusable();
-    method public void onRestoreInstanceState(android.os.Bundle!, String!);
-    method public void onSaveInstanceState(android.os.Bundle!, String!);
+    method public void onRestoreInstanceState(android.os.Bundle, String);
+    method public void onSaveInstanceState(android.os.Bundle, String);
     method public void setChecked(boolean);
-    method public void setDescription(CharSequence!);
-    method public void setEditDescription(CharSequence!);
-    method public void setEditTitle(CharSequence!);
+    method public void setDescription(CharSequence?);
+    method public void setEditDescription(CharSequence?);
+    method public void setEditTitle(CharSequence?);
     method public void setEnabled(boolean);
     method public void setFocusable(boolean);
-    method public void setIntent(android.content.Intent!);
-    method public void setSubActions(java.util.List<androidx.leanback.widget.GuidedAction!>!);
-    method public void setTitle(CharSequence!);
+    method public void setIntent(android.content.Intent?);
+    method public void setSubActions(java.util.List<androidx.leanback.widget.GuidedAction!>?);
+    method public void setTitle(CharSequence?);
     field public static final long ACTION_ID_CANCEL = -5L; // 0xfffffffffffffffbL
     field public static final long ACTION_ID_CONTINUE = -7L; // 0xfffffffffffffff9L
     field public static final long ACTION_ID_CURRENT = -3L; // 0xfffffffffffffffdL
@@ -2004,44 +2004,44 @@
 
   public static class GuidedAction.Builder extends androidx.leanback.widget.GuidedAction.BuilderBase<androidx.leanback.widget.GuidedAction.Builder> {
     ctor @Deprecated public GuidedAction.Builder();
-    ctor public GuidedAction.Builder(android.content.Context!);
-    method public androidx.leanback.widget.GuidedAction! build();
+    ctor public GuidedAction.Builder(android.content.Context?);
+    method public androidx.leanback.widget.GuidedAction build();
   }
 
   public abstract static class GuidedAction.BuilderBase<B extends androidx.leanback.widget.GuidedAction.BuilderBase> {
-    ctor public GuidedAction.BuilderBase(android.content.Context!);
-    method protected final void applyValues(androidx.leanback.widget.GuidedAction!);
+    ctor public GuidedAction.BuilderBase(android.content.Context);
+    method protected final void applyValues(androidx.leanback.widget.GuidedAction);
     method public B! autoSaveRestoreEnabled(boolean);
     method public B! autofillHints(java.lang.String!...);
     method public B! checkSetId(int);
     method public B! checked(boolean);
     method public B! clickAction(long);
-    method public B! description(CharSequence!);
+    method public B! description(CharSequence?);
     method public B! description(@StringRes int);
     method public B! descriptionEditInputType(int);
     method public B! descriptionEditable(boolean);
     method public B! descriptionInputType(int);
-    method public B! editDescription(CharSequence!);
+    method public B! editDescription(CharSequence?);
     method public B! editDescription(@StringRes int);
     method public B! editInputType(int);
-    method public B! editTitle(CharSequence!);
+    method public B! editTitle(CharSequence?);
     method public B! editTitle(@StringRes int);
     method public B! editable(boolean);
     method public B! enabled(boolean);
     method public B! focusable(boolean);
-    method public android.content.Context! getContext();
+    method public android.content.Context getContext();
     method public B! hasEditableActivatorView(boolean);
     method public B! hasNext(boolean);
-    method public B! icon(android.graphics.drawable.Drawable!);
+    method public B! icon(android.graphics.drawable.Drawable?);
     method public B! icon(@DrawableRes int);
     method @Deprecated public B! iconResourceId(@DrawableRes int, android.content.Context!);
     method public B! id(long);
     method public B! infoOnly(boolean);
     method public B! inputType(int);
-    method public B! intent(android.content.Intent!);
+    method public B! intent(android.content.Intent?);
     method public B! multilineDescription(boolean);
-    method public B! subActions(java.util.List<androidx.leanback.widget.GuidedAction!>!);
-    method public B! title(CharSequence!);
+    method public B! subActions(java.util.List<androidx.leanback.widget.GuidedAction!>?);
+    method public B! title(CharSequence?);
     method public B! title(@StringRes int);
   }
 
@@ -2081,13 +2081,13 @@
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class GuidedActionAdapterGroup {
     ctor public GuidedActionAdapterGroup();
-    method public void addAdpter(androidx.leanback.widget.GuidedActionAdapter!, androidx.leanback.widget.GuidedActionAdapter!);
-    method public void closeIme(android.view.View!);
-    method public void fillAndGoNext(androidx.leanback.widget.GuidedActionAdapter!, android.widget.TextView!);
-    method public void fillAndStay(androidx.leanback.widget.GuidedActionAdapter!, android.widget.TextView!);
-    method public androidx.leanback.widget.GuidedActionAdapter! getNextAdapter(androidx.leanback.widget.GuidedActionAdapter!);
-    method public void openIme(androidx.leanback.widget.GuidedActionAdapter!, androidx.leanback.widget.GuidedActionsStylist.ViewHolder!);
-    method public void setEditListener(androidx.leanback.widget.GuidedActionAdapter.EditListener!);
+    method public void addAdpter(androidx.leanback.widget.GuidedActionAdapter?, androidx.leanback.widget.GuidedActionAdapter?);
+    method public void closeIme(android.view.View);
+    method public void fillAndGoNext(androidx.leanback.widget.GuidedActionAdapter, android.widget.TextView);
+    method public void fillAndStay(androidx.leanback.widget.GuidedActionAdapter, android.widget.TextView);
+    method public androidx.leanback.widget.GuidedActionAdapter? getNextAdapter(androidx.leanback.widget.GuidedActionAdapter);
+    method public void openIme(androidx.leanback.widget.GuidedActionAdapter, androidx.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public void setEditListener(androidx.leanback.widget.GuidedActionAdapter.EditListener?);
   }
 
   public interface GuidedActionAutofillSupport {
@@ -3033,19 +3033,19 @@
   }
 
   public class SearchOrbView extends android.widget.FrameLayout implements android.view.View.OnClickListener {
-    ctor public SearchOrbView(android.content.Context!);
-    ctor public SearchOrbView(android.content.Context!, android.util.AttributeSet!);
-    ctor public SearchOrbView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public SearchOrbView(android.content.Context);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet?);
+    ctor public SearchOrbView(android.content.Context, android.util.AttributeSet?, int);
     method public void enableOrbColorAnimation(boolean);
     method @ColorInt public int getOrbColor();
-    method public androidx.leanback.widget.SearchOrbView.Colors! getOrbColors();
-    method public android.graphics.drawable.Drawable! getOrbIcon();
+    method public androidx.leanback.widget.SearchOrbView.Colors? getOrbColors();
+    method public android.graphics.drawable.Drawable? getOrbIcon();
     method public void onClick(android.view.View!);
-    method public void setOnOrbClickedListener(android.view.View.OnClickListener!);
+    method public void setOnOrbClickedListener(android.view.View.OnClickListener?);
     method public void setOrbColor(int);
     method @Deprecated public void setOrbColor(@ColorInt int, @ColorInt int);
-    method public void setOrbColors(androidx.leanback.widget.SearchOrbView.Colors!);
-    method public void setOrbIcon(android.graphics.drawable.Drawable!);
+    method public void setOrbColors(androidx.leanback.widget.SearchOrbView.Colors);
+    method public void setOrbIcon(android.graphics.drawable.Drawable);
   }
 
   public static class SearchOrbView.Colors {
@@ -3210,19 +3210,19 @@
   }
 
   public class TitleView extends android.widget.FrameLayout implements androidx.leanback.widget.TitleViewAdapter.Provider {
-    ctor public TitleView(android.content.Context!);
-    ctor public TitleView(android.content.Context!, android.util.AttributeSet!);
-    ctor public TitleView(android.content.Context!, android.util.AttributeSet!, int);
+    ctor public TitleView(android.content.Context);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet?);
+    ctor public TitleView(android.content.Context, android.util.AttributeSet?, int);
     method public void enableAnimation(boolean);
-    method public android.graphics.drawable.Drawable! getBadgeDrawable();
-    method public androidx.leanback.widget.SearchOrbView.Colors! getSearchAffordanceColors();
-    method public android.view.View! getSearchAffordanceView();
-    method public CharSequence! getTitle();
-    method public androidx.leanback.widget.TitleViewAdapter! getTitleViewAdapter();
-    method public void setBadgeDrawable(android.graphics.drawable.Drawable!);
-    method public void setOnSearchClickedListener(android.view.View.OnClickListener!);
-    method public void setSearchAffordanceColors(androidx.leanback.widget.SearchOrbView.Colors!);
-    method public void setTitle(CharSequence!);
+    method public android.graphics.drawable.Drawable? getBadgeDrawable();
+    method public androidx.leanback.widget.SearchOrbView.Colors? getSearchAffordanceColors();
+    method public android.view.View getSearchAffordanceView();
+    method public CharSequence? getTitle();
+    method public androidx.leanback.widget.TitleViewAdapter getTitleViewAdapter();
+    method public void setBadgeDrawable(android.graphics.drawable.Drawable?);
+    method public void setOnSearchClickedListener(android.view.View.OnClickListener?);
+    method public void setSearchAffordanceColors(androidx.leanback.widget.SearchOrbView.Colors);
+    method public void setTitle(CharSequence?);
     method public void updateComponentsVisibility(int);
   }
 
diff --git a/leanback/leanback/src/main/java/androidx/leanback/app/VerticalGridSupportFragment.java b/leanback/leanback/src/main/java/androidx/leanback/app/VerticalGridSupportFragment.java
index 26b9a648..764f988 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/app/VerticalGridSupportFragment.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/app/VerticalGridSupportFragment.java
@@ -19,6 +19,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.leanback.R;
 import androidx.leanback.transition.TransitionHelper;
 import androidx.leanback.util.StateMachine.State;
@@ -76,7 +78,7 @@
     /**
      * Sets the grid presenter.
      */
-    public void setGridPresenter(VerticalGridPresenter gridPresenter) {
+    public void setGridPresenter(@NonNull VerticalGridPresenter gridPresenter) {
         if (gridPresenter == null) {
             throw new IllegalArgumentException("Grid presenter may not be null");
         }
@@ -90,6 +92,7 @@
     /**
      * Returns the grid presenter.
      */
+    @Nullable
     public VerticalGridPresenter getGridPresenter() {
         return mGridPresenter;
     }
@@ -97,7 +100,7 @@
     /**
      * Sets the object adapter for the fragment.
      */
-    public void setAdapter(ObjectAdapter adapter) {
+    public void setAdapter(@Nullable ObjectAdapter adapter) {
         mAdapter = adapter;
         updateAdapter();
     }
@@ -105,6 +108,7 @@
     /**
      * Returns the object adapter.
      */
+    @Nullable
     public ObjectAdapter getAdapter() {
         return mAdapter;
     }
@@ -127,7 +131,12 @@
     final private OnChildLaidOutListener mChildLaidOutListener =
             new OnChildLaidOutListener() {
         @Override
-        public void onChildLaidOut(ViewGroup parent, View view, int position, long id) {
+        public void onChildLaidOut(
+                @NonNull ViewGroup parent,
+                @NonNull View view,
+                int position,
+                long id
+        ) {
             if (position == 0) {
                 showOrHideTitle();
             }
@@ -137,7 +146,7 @@
     /**
      * Sets an item selection listener.
      */
-    public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
+    public void setOnItemViewSelectedListener(@Nullable OnItemViewSelectedListener listener) {
         mOnItemViewSelectedListener = listener;
     }
 
@@ -163,7 +172,7 @@
     /**
      * Sets an item clicked listener.
      */
-    public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
+    public void setOnItemViewClickedListener(@Nullable OnItemViewClickedListener listener) {
         mOnItemViewClickedListener = listener;
         if (mGridPresenter != null) {
             mGridPresenter.setOnItemViewClickedListener(mOnItemViewClickedListener);
@@ -173,13 +182,15 @@
     /**
      * Returns the item clicked listener.
      */
+    @Nullable
     public OnItemViewClickedListener getOnItemViewClickedListener() {
         return mOnItemViewClickedListener;
     }
 
     @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
+    @NonNull
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
         ViewGroup root = (ViewGroup) inflater.inflate(R.layout.lb_vertical_grid_fragment,
                 container, false);
         ViewGroup gridFrame = (ViewGroup) root.findViewById(R.id.grid_frame);
@@ -242,13 +253,14 @@
     }
 
     @Override
+    @NonNull
     protected Object createEntranceTransition() {
         return TransitionHelper.loadTransition(getContext(),
                 R.transition.lb_vertical_grid_entrance_transition);
     }
 
     @Override
-    protected void runEntranceTransition(Object entranceTransition) {
+    protected void runEntranceTransition(@Nullable Object entranceTransition) {
         TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
     }
 
diff --git a/leanback/leanback/src/main/java/androidx/leanback/graphics/CompositeDrawable.java b/leanback/leanback/src/main/java/androidx/leanback/graphics/CompositeDrawable.java
index 196b94a..0b0b320 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/graphics/CompositeDrawable.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/graphics/CompositeDrawable.java
@@ -24,6 +24,7 @@
 import android.util.Property;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.core.graphics.drawable.DrawableCompat;
 import androidx.leanback.graphics.BoundsRule.ValueRule;
 
@@ -76,11 +77,13 @@
     }
 
     @Override
+    @NonNull
     public ConstantState getConstantState() {
         return mState;
     }
 
     @Override
+    @NonNull
     public Drawable mutate() {
         if (!mMutated && super.mutate() == this) {
             mState = new CompositeState(mState, this, null);
@@ -99,20 +102,21 @@
     /**
      * Adds the supplied region.
      */
-    public void addChildDrawable(Drawable drawable) {
+    public void addChildDrawable(@NonNull Drawable drawable) {
         mState.mChildren.add(new ChildDrawable(drawable, this));
     }
 
     /**
      * Sets the supplied region at given index.
      */
-    public void setChildDrawableAt(int index, Drawable drawable) {
+    public void setChildDrawableAt(int index, @NonNull Drawable drawable) {
         mState.mChildren.set(index, new ChildDrawable(drawable, this));
     }
 
     /**
      * Returns the {@link Drawable} for the given index.
      */
+    @NonNull
     public Drawable getDrawable(int index) {
         return mState.mChildren.get(index).mDrawable;
     }
@@ -120,6 +124,7 @@
     /**
      * Returns the {@link ChildDrawable} at the given index.
      */
+    @NonNull
     public ChildDrawable getChildAt(int index) {
         return mState.mChildren.get(index);
     }
@@ -134,7 +139,7 @@
     /**
      * Removes the given region.
      */
-    public void removeDrawable(Drawable drawable) {
+    public void removeDrawable(@NonNull Drawable drawable) {
         final ArrayList<ChildDrawable> children = mState.mChildren;
         for (int i = 0; i < children.size(); i++) {
             if (drawable == children.get(i).mDrawable) {
@@ -153,7 +158,7 @@
     }
 
     @Override
-    public void draw(Canvas canvas) {
+    public void draw(@NonNull Canvas canvas) {
         final ArrayList<ChildDrawable> children = mState.mChildren;
         for (int i = 0; i < children.size(); i++) {
             children.get(i).mDrawable.draw(canvas);
@@ -167,7 +172,7 @@
     }
 
     @Override
-    public void setColorFilter(ColorFilter colorFilter) {
+    public void setColorFilter(@Nullable ColorFilter colorFilter) {
         final ArrayList<ChildDrawable> children = mState.mChildren;
         for (int i = 0; i < children.size(); i++) {
             children.get(i).mDrawable.setColorFilter(colorFilter);
@@ -212,17 +217,17 @@
     }
 
     @Override
-    public void invalidateDrawable(Drawable who) {
+    public void invalidateDrawable(@NonNull Drawable who) {
         invalidateSelf();
     }
 
     @Override
-    public void scheduleDrawable(Drawable who, Runnable what, long when) {
+    public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
         scheduleSelf(what, when);
     }
 
     @Override
-    public void unscheduleDrawable(Drawable who, Runnable what) {
+    public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
         unscheduleSelf(what);
     }
 
@@ -247,7 +252,7 @@
         private final Rect adjustedBounds = new Rect();
         final CompositeDrawable mParent;
 
-        public ChildDrawable(Drawable drawable, CompositeDrawable parent) {
+        public ChildDrawable(@NonNull Drawable drawable, @NonNull CompositeDrawable parent) {
             this.mDrawable = drawable;
             this.mParent = parent;
             this.mBoundsRule = new BoundsRule();
@@ -283,6 +288,7 @@
         /**
          * Returns the instance of {@link BoundsRule}.
          */
+        @NonNull
         public BoundsRule getBoundsRule() {
             return this.mBoundsRule;
         }
@@ -290,6 +296,7 @@
         /**
          * Returns the {@link Drawable}.
          */
+        @NonNull
         public Drawable getDrawable() {
             return mDrawable;
         }
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/DetailsOverviewLogoPresenter.java b/leanback/leanback/src/main/java/androidx/leanback/widget/DetailsOverviewLogoPresenter.java
index e2017b3..6f62961 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/DetailsOverviewLogoPresenter.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/DetailsOverviewLogoPresenter.java
@@ -5,6 +5,8 @@
 import android.view.ViewGroup;
 import android.widget.ImageView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.leanback.R;
 
 /**
@@ -29,18 +31,22 @@
      */
     public static class ViewHolder extends Presenter.ViewHolder {
 
+        @Nullable
         protected FullWidthDetailsOverviewRowPresenter mParentPresenter;
+        @Nullable
         protected FullWidthDetailsOverviewRowPresenter.ViewHolder mParentViewHolder;
         private boolean mSizeFromDrawableIntrinsic;
 
-        public ViewHolder(View view) {
+        public ViewHolder(@NonNull View view) {
             super(view);
         }
 
+        @Nullable
         public FullWidthDetailsOverviewRowPresenter getParentPresenter() {
             return mParentPresenter;
         }
 
+        @Nullable
         public FullWidthDetailsOverviewRowPresenter.ViewHolder getParentViewHolder() {
             return mParentViewHolder;
         }
@@ -84,13 +90,15 @@
      * @param parent Parent view.
      * @return View created for the logo.
      */
-    public View onCreateView(ViewGroup parent) {
+    @NonNull
+    public View onCreateView(@NonNull ViewGroup parent) {
         return LayoutInflater.from(parent.getContext())
                 .inflate(R.layout.lb_fullwidth_details_overview_logo, parent, false);
     }
 
+    @NonNull
     @Override
-    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
+    public Presenter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent) {
         View view = onCreateView(parent);
         ViewHolder vh = new ViewHolder(view);
         ViewGroup.LayoutParams lp = view.getLayoutParams();
@@ -106,9 +114,9 @@
      * @param parentViewHolder
      * @param parentPresenter
      */
-    public void setContext(ViewHolder viewHolder,
-            FullWidthDetailsOverviewRowPresenter.ViewHolder parentViewHolder,
-            FullWidthDetailsOverviewRowPresenter parentPresenter) {
+    public void setContext(@NonNull ViewHolder viewHolder,
+            @Nullable FullWidthDetailsOverviewRowPresenter.ViewHolder parentViewHolder,
+            @Nullable FullWidthDetailsOverviewRowPresenter parentPresenter) {
         viewHolder.mParentViewHolder = parentViewHolder;
         viewHolder.mParentPresenter = parentPresenter;
     }
@@ -121,7 +129,10 @@
      * {@link FullWidthDetailsOverviewRowPresenter#notifyOnBindLogo(FullWidthDetailsOverviewRowPresenter.ViewHolder)}
      * when image view is bound to the drawable.
      */
-    public boolean isBoundToImage(ViewHolder viewHolder, DetailsOverviewRow row) {
+    public boolean isBoundToImage(
+            @NonNull ViewHolder viewHolder,
+            @Nullable DetailsOverviewRow row
+    ) {
         return row != null && row.getImageDrawable() != null;
     }
 
@@ -133,7 +144,7 @@
      * @param item DetailsOverviewRow object to bind.
      */
     @Override
-    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
+    public void onBindViewHolder(@NonNull Presenter.ViewHolder viewHolder, @NonNull Object item) {
         DetailsOverviewRow row = (DetailsOverviewRow) item;
         ImageView imageView = ((ImageView) viewHolder.view);
         imageView.setImageDrawable(row.getImageDrawable());
@@ -167,7 +178,7 @@
     }
 
     @Override
-    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
+    public void onUnbindViewHolder(@NonNull Presenter.ViewHolder viewHolder) {
     }
 
 }
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedAction.java b/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedAction.java
index c2857b2..bb05177 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedAction.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedAction.java
@@ -13,6 +13,7 @@
  */
 package androidx.leanback.widget;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
@@ -20,6 +21,8 @@
 import android.text.InputType;
 
 import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 import androidx.core.content.ContextCompat;
 import androidx.leanback.R;
@@ -142,7 +145,7 @@
          * Creates a BuilderBase for GuidedAction or its subclass.
          * @param context Context object used to build the GuidedAction.
          */
-        public BuilderBase(Context context) {
+        public BuilderBase(@NonNull Context context) {
             mContext = context;
             mActionFlags = PF_ENABLED | PF_FOCUSABLE | PF_AUTORESTORE;
         }
@@ -151,6 +154,7 @@
          * Returns Context of this Builder.
          * @return Context of this Builder.
          */
+        @NonNull
         public Context getContext() {
             return mContext;
         }
@@ -163,7 +167,7 @@
          * Subclass of BuilderBase should call this function to apply values.
          * @param action GuidedAction to apply BuilderBase values.
          */
-        protected final void applyValues(GuidedAction action) {
+        protected final void applyValues(@NonNull GuidedAction action) {
             // Base Action values
             action.setId(mId);
             action.setLabel1(mTitle);
@@ -231,7 +235,7 @@
          * action to be taken on click, e.g. "Continue" or "Cancel".
          * @param title The title for this action.
          */
-        public B title(CharSequence title) {
+        public B title(@Nullable CharSequence title) {
             mTitle = title;
             return (B) this;
         }
@@ -251,7 +255,7 @@
          * replaces the string of title.
          * @param editTitle The optional title text to edit when TextView is activated.
          */
-        public B editTitle(CharSequence editTitle) {
+        public B editTitle(@Nullable CharSequence editTitle) {
             mEditTitle = editTitle;
             return (B) this;
         }
@@ -272,7 +276,7 @@
          * providing extra information on what the action will do.
          * @param description The description for this action.
          */
-        public B description(CharSequence description) {
+        public B description(@Nullable CharSequence description) {
             mDescription = description;
             return (B) this;
         }
@@ -292,7 +296,7 @@
          * description replaces the string of description.
          * @param description The description to edit for this action.
          */
-        public B editDescription(CharSequence description) {
+        public B editDescription(@Nullable CharSequence description) {
             mEditDescription = description;
             return (B) this;
         }
@@ -313,7 +317,7 @@
          * directly when the action is clicked.
          * @param intent The intent associated with this action.
          */
-        public B intent(Intent intent) {
+        public B intent(@Nullable Intent intent) {
             mIntent = intent;
             return (B) this;
         }
@@ -322,7 +326,7 @@
          * Sets the action's icon drawable.
          * @param icon The drawable for the icon associated with this action.
          */
-        public B icon(Drawable icon) {
+        public B icon(@Nullable Drawable icon) {
             mIcon = icon;
             return (B) this;
         }
@@ -530,7 +534,7 @@
          * @param subActions
          * @return The same BuilderBase object.
          */
-        public B subActions(List<GuidedAction> subActions) {
+        public B subActions(@Nullable List<GuidedAction> subActions) {
             mSubActions = subActions;
             return (B) this;
         }
@@ -552,7 +556,7 @@
          * @param hints List of hints for autofill.
          * @return The same BuilderBase object.
          */
-        public B autofillHints(String... hints) {
+        public B autofillHints(@Nullable String... hints) {
             mAutofillHints = hints;
             return (B) this;
         }
@@ -575,7 +579,7 @@
          * Creates a Builder for GuidedAction.
          * @param context Context to build GuidedAction.
          */
-        public Builder(Context context) {
+        public Builder(@Nullable Context context) {
             super(context);
         }
 
@@ -583,6 +587,7 @@
          * Builds the GuidedAction corresponding to this Builder.
          * @return The GuidedAction as configured through this Builder.
          */
+        @NonNull
         public GuidedAction build() {
             GuidedAction action = new GuidedAction();
             applyValues(action);
@@ -627,6 +632,7 @@
      * Returns the title of this action.
      * @return The title set when this action was built.
      */
+    @Nullable
     public CharSequence getTitle() {
         return getLabel1();
     }
@@ -635,7 +641,7 @@
      * Sets the title of this action.
      * @param title The title set when this action was built.
      */
-    public void setTitle(CharSequence title) {
+    public void setTitle(@Nullable CharSequence title) {
         setLabel1(title);
     }
 
@@ -644,6 +650,7 @@
      * {@link #getTitle()}.
      * @return Optional title text to edit instead of {@link #getTitle()}.
      */
+    @Nullable
     public CharSequence getEditTitle() {
         return mEditTitle;
     }
@@ -652,7 +659,7 @@
      * Sets the optional title text to edit instead of {@link #setTitle(CharSequence)}.
      * @param editTitle Optional title text to edit instead of {@link #setTitle(CharSequence)}.
      */
-    public void setEditTitle(CharSequence editTitle) {
+    public void setEditTitle(@Nullable CharSequence editTitle) {
         mEditTitle = editTitle;
     }
 
@@ -661,6 +668,7 @@
      * {@link #getDescription()}.
      * @return Optional description text to edit instead of {@link #getDescription()}.
      */
+    @Nullable
     public CharSequence getEditDescription() {
         return mEditDescription;
     }
@@ -670,7 +678,7 @@
      * @param editDescription Optional description text to edit instead of
      * {@link #setDescription(CharSequence)}.
      */
-    public void setEditDescription(CharSequence editDescription) {
+    public void setEditDescription(@Nullable CharSequence editDescription) {
         mEditDescription = editDescription;
     }
 
@@ -687,6 +695,7 @@
      * Returns the description of this action.
      * @return The description of this action.
      */
+    @Nullable
     public CharSequence getDescription() {
         return getLabel2();
     }
@@ -695,7 +704,7 @@
      * Sets the description of this action.
      * @param description The description of the action.
      */
-    public void setDescription(CharSequence description) {
+    public void setDescription(@Nullable CharSequence description) {
         setLabel2(description);
     }
 
@@ -703,6 +712,7 @@
      * Returns the intent associated with this action.
      * @return The intent set when this action was built.
      */
+    @Nullable
     public Intent getIntent() {
         return mIntent;
     }
@@ -711,7 +721,7 @@
      * Sets the intent of this action.
      * @param intent New intent to set on this action.
      */
-    public void setIntent(Intent intent) {
+    public void setIntent(@Nullable Intent intent) {
         mIntent = intent;
     }
 
@@ -881,13 +891,15 @@
      * Change sub actions list.
      * @param actions Sub actions list to set on this action.  Sets null to disable sub actions.
      */
-    public void setSubActions(List<GuidedAction> actions) {
+    public void setSubActions(@Nullable List<GuidedAction> actions) {
         mSubActions = actions;
     }
 
     /**
      * @return List of sub actions or null if sub actions list is not enabled.
      */
+    @SuppressLint("NullableCollection")
+    @Nullable
     public List<GuidedAction> getSubActions() {
         return mSubActions;
     }
@@ -929,7 +941,7 @@
      * @param bundle  Bundle to save the Action.
      * @param key Key used to save the Action.
      */
-    public void onSaveInstanceState(Bundle bundle, String key) {
+    public void onSaveInstanceState(@NonNull Bundle bundle, @NonNull String key) {
         if (needAutoSaveTitle() && getTitle() != null) {
             bundle.putString(key, getTitle().toString());
         } else if (needAutoSaveDescription() && getDescription() != null) {
@@ -951,7 +963,7 @@
      * @param bundle  Bundle to restore the Action from.
      * @param key Key used to restore the Action.
      */
-    public void onRestoreInstanceState(Bundle bundle, String key) {
+    public void onRestoreInstanceState(@NonNull Bundle bundle, @NonNull String key) {
         if (needAutoSaveTitle()) {
             String title = bundle.getString(key);
             if (title != null) {
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionAdapterGroup.java b/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionAdapterGroup.java
index f4e16be..180ea9d 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionAdapterGroup.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/GuidedActionAdapterGroup.java
@@ -22,6 +22,8 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.leanback.widget.GuidedActionAdapter.EditListener;
 
@@ -43,7 +45,10 @@
     private boolean mImeOpened;
     private EditListener mEditListener;
 
-    public void addAdpter(GuidedActionAdapter adapter1, GuidedActionAdapter adapter2) {
+    public void addAdpter(
+            @Nullable GuidedActionAdapter adapter1,
+            @Nullable GuidedActionAdapter adapter2
+    ) {
         mAdapters.add(new Pair<GuidedActionAdapter, GuidedActionAdapter>(adapter1, adapter2));
         if (adapter1 != null) {
             adapter1.mGroup = this;
@@ -53,7 +58,8 @@
         }
     }
 
-    public GuidedActionAdapter getNextAdapter(GuidedActionAdapter adapter) {
+    @Nullable
+    public GuidedActionAdapter getNextAdapter(@NonNull GuidedActionAdapter adapter) {
         for (int i = 0; i < mAdapters.size(); i++) {
             Pair<GuidedActionAdapter, GuidedActionAdapter> pair = mAdapters.get(i);
             if (pair.first == adapter) {
@@ -63,7 +69,7 @@
         return null;
     }
 
-    public void setEditListener(EditListener listener) {
+    public void setEditListener(@Nullable EditListener listener) {
         mEditListener = listener;
     }
 
@@ -119,7 +125,10 @@
         return false;
     }
 
-    public void openIme(GuidedActionAdapter adapter, GuidedActionsStylist.ViewHolder avh) {
+    public void openIme(
+            @NonNull GuidedActionAdapter adapter,
+            @NonNull GuidedActionsStylist.ViewHolder avh
+    ) {
         adapter.getGuidedActionsStylist().setEditingMode(avh, true);
         View v = avh.getEditingView();
         if (v == null || !avh.isInEditingText()) {
@@ -138,7 +147,7 @@
         }
     }
 
-    public void closeIme(View v) {
+    public void closeIme(@NonNull View v) {
         if (mImeOpened) {
             mImeOpened = false;
             InputMethodManager mgr = (InputMethodManager)
@@ -148,7 +157,7 @@
         }
     }
 
-    public void fillAndStay(GuidedActionAdapter adapter, TextView v) {
+    public void fillAndStay(@NonNull GuidedActionAdapter adapter, @NonNull TextView v) {
         GuidedActionsStylist.ViewHolder avh = adapter.findSubChildViewHolder(v);
         updateTextIntoAction(avh, v);
         mEditListener.onGuidedActionEditCanceled(avh.getAction());
@@ -157,7 +166,7 @@
         avh.itemView.requestFocus();
     }
 
-    public void fillAndGoNext(GuidedActionAdapter adapter, TextView v) {
+    public void fillAndGoNext(@NonNull GuidedActionAdapter adapter, @NonNull TextView v) {
         boolean handled = false;
         GuidedActionsStylist.ViewHolder avh = adapter.findSubChildViewHolder(v);
         updateTextIntoAction(avh, v);
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/SearchOrbView.java b/leanback/leanback/src/main/java/androidx/leanback/widget/SearchOrbView.java
index 5870c24..870bbf5 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/SearchOrbView.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/SearchOrbView.java
@@ -33,6 +33,8 @@
 import android.widget.ImageView;
 
 import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.core.view.ViewCompat;
 import androidx.leanback.R;
 
@@ -145,16 +147,16 @@
         ViewCompat.setZ(mSearchOrbView, mUnfocusedZ + fraction * (mFocusedZ - mUnfocusedZ));
     }
 
-    public SearchOrbView(Context context) {
+    public SearchOrbView(@NonNull Context context) {
         this(context, null);
     }
 
-    public SearchOrbView(Context context, AttributeSet attrs) {
+    public SearchOrbView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.searchOrbViewStyle);
     }
 
     @SuppressLint("CustomViewStyleable")
-    public SearchOrbView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public SearchOrbView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
         final Resources res = context.getResources();
@@ -240,7 +242,11 @@
     }
 
     @Override
-    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+    protected void onFocusChanged(
+            boolean gainFocus,
+            int direction,
+            @Nullable Rect previouslyFocusedRect
+    ) {
         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
         animateOnFocus(gainFocus);
     }
@@ -257,7 +263,7 @@
      *
      * @param icon the drawable to be used as the icon
      */
-    public void setOrbIcon(Drawable icon) {
+    public void setOrbIcon(@NonNull Drawable icon) {
         mIconDrawable = icon;
         mIcon.setImageDrawable(mIconDrawable);
     }
@@ -267,6 +273,7 @@
      *
      * @return the drawable used as the icon
      */
+    @Nullable
     public Drawable getOrbIcon() {
         return mIconDrawable;
     }
@@ -276,7 +283,7 @@
      *
      * @param listener The listener.
      */
-    public void setOnOrbClickedListener(OnClickListener listener) {
+    public void setOnOrbClickedListener(@Nullable OnClickListener listener) {
         mListener = listener;
     }
 
@@ -314,7 +321,7 @@
     /**
      * Sets the {@link Colors} used to display the search orb.
      */
-    public void setOrbColors(Colors colors) {
+    public void setOrbColors(@NonNull Colors colors) {
         mColors = colors;
         mIcon.setColorFilter(mColors.iconColor);
 
@@ -328,6 +335,7 @@
     /**
      * Returns the {@link Colors} used to display the search orb.
      */
+    @Nullable
     public Colors getOrbColors() {
         return mColors;
     }
diff --git a/leanback/leanback/src/main/java/androidx/leanback/widget/TitleView.java b/leanback/leanback/src/main/java/androidx/leanback/widget/TitleView.java
index 05eaa9a..f96033f 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/widget/TitleView.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/widget/TitleView.java
@@ -26,6 +26,8 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.leanback.R;
 
 /**
@@ -91,15 +93,15 @@
         }
     };
 
-    public TitleView(Context context) {
+    public TitleView(@NonNull Context context) {
         this(context, null);
     }
 
-    public TitleView(Context context, AttributeSet attrs) {
+    public TitleView(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, R.attr.browseTitleViewStyle);
     }
 
-    public TitleView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public TitleView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
         LayoutInflater inflater = LayoutInflater.from(context);
@@ -116,7 +118,7 @@
     /**
      * Sets the title text.
      */
-    public void setTitle(CharSequence titleText) {
+    public void setTitle(@Nullable CharSequence titleText) {
         mTextView.setText(titleText);
         updateBadgeVisibility();
     }
@@ -124,6 +126,7 @@
     /**
      * Returns the title text.
      */
+    @Nullable
     public CharSequence getTitle() {
         return mTextView.getText();
     }
@@ -132,7 +135,7 @@
      * Sets the badge drawable.
      * If non-null, the drawable is displayed instead of the title text.
      */
-    public void setBadgeDrawable(Drawable drawable) {
+    public void setBadgeDrawable(@Nullable Drawable drawable) {
         mBadgeView.setImageDrawable(drawable);
         updateBadgeVisibility();
     }
@@ -140,6 +143,7 @@
     /**
      * Returns the badge drawable.
      */
+    @Nullable
     public Drawable getBadgeDrawable() {
         return mBadgeView.getDrawable();
     }
@@ -147,7 +151,7 @@
     /**
      * Sets the listener to be called when the search affordance is clicked.
      */
-    public void setOnSearchClickedListener(View.OnClickListener listener) {
+    public void setOnSearchClickedListener(@Nullable OnClickListener listener) {
         mHasSearchListener = listener != null;
         mSearchOrbView.setOnOrbClickedListener(listener);
         updateSearchOrbViewVisiblity();
@@ -156,6 +160,7 @@
     /**
      *  Returns the view for the search affordance.
      */
+    @NonNull
     public View getSearchAffordanceView() {
         return mSearchOrbView;
     }
@@ -163,13 +168,14 @@
     /**
      * Sets the {@link SearchOrbView.Colors} used to draw the search affordance.
      */
-    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
+    public void setSearchAffordanceColors(@NonNull SearchOrbView.Colors colors) {
         mSearchOrbView.setOrbColors(colors);
     }
 
     /**
      * Returns the {@link SearchOrbView.Colors} used to draw the search affordance.
      */
+    @Nullable
     public SearchOrbView.Colors getSearchAffordanceColors() {
         return mSearchOrbView.getOrbColors();
     }
@@ -220,6 +226,7 @@
     }
 
     @Override
+    @NonNull
     public TitleViewAdapter getTitleViewAdapter() {
         return mTitleViewAdapter;
     }
diff --git a/libraryversions.toml b/libraryversions.toml
index 25d31b2..c255e43 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -138,7 +138,7 @@
 ACTIVITY = { group = "androidx.activity", atomicGroupVersion = "versions.ACTIVITY" }
 ADS = { group = "androidx.ads" }
 ANNOTATION = { group = "androidx.annotation" }
-APPCOMPAT = { group = "androidx.appcompat" }
+APPCOMPAT = { group = "androidx.appcompat", atomicGroupVersion = "versions.APPCOMPAT" }
 APPSEARCH = { group = "androidx.appsearch", atomicGroupVersion = "versions.APPSEARCH" }
 ARCH_CORE = { group = "androidx.arch.core", atomicGroupVersion = "versions.ARCH_CORE" }
 ASYNCLAYOUTINFLATER = { group = "androidx.asynclayoutinflater", atomicGroupVersion = "versions.ASYNCLAYOUTINFLATER" }
diff --git a/playground-common/gradle/wrapper/gradle-wrapper.properties b/playground-common/gradle/wrapper/gradle-wrapper.properties
index 26e62a1..7e65b70 100644
--- a/playground-common/gradle/wrapper/gradle-wrapper.properties
+++ b/playground-common/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-rc-2-bin.zip
+distributionSha256Sum=ba761aa1563f5d893d1a6e7ddca48bbdc4385fdd527974e6472b873b7eae9a32
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt
index b0dd98d..c9ca6f5 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt
@@ -15,12 +15,15 @@
  */
 package androidx.room.compiler.processing
 
+import java.lang.Character.isISOControl
+import com.squareup.javapoet.AnnotationSpec
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.MethodSpec
 import com.squareup.javapoet.ParameterSpec
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
 import com.squareup.javapoet.TypeSpec
+import javax.lang.model.SourceVersion
 import javax.lang.model.element.Modifier
 import javax.lang.model.type.TypeKind
 import javax.lang.model.type.TypeMirror
@@ -36,6 +39,51 @@
  */
 private val NONE_TYPE_NAME = ClassName.get("androidx.room.compiler.processing.error", "NotAType")
 
+fun XAnnotation.toAnnotationSpec(): AnnotationSpec {
+  val builder = AnnotationSpec.builder(className)
+  annotationValues.forEach { builder.visitAnnotationValue(it) }
+  return builder.build()
+}
+
+private fun AnnotationSpec.Builder.visitAnnotationValue(annotationValue: XAnnotationValue) {
+  val name = annotationValue.name
+  when (val value = annotationValue.value) {
+    is XAnnotation -> addMember(name, "\$L", value.toAnnotationSpec())
+    is XVariableElement -> addMember(name, "\$T.\$L", value.type.typeName, value.name)
+    is XType -> addMember(name, "\$T.class", value.typeName)
+    is List<*> -> value.forEach { if (it is XAnnotationValue) { visitAnnotationValue(it) } }
+    else -> this.addMemberForCommonValue(name, value)
+  }
+}
+
+private fun AnnotationSpec.Builder.addMemberForCommonValue(memberName: String, value: Any?) {
+    requireNotNull(value) { "value == null, constant non-null value expected for $memberName" }
+    require(SourceVersion.isName(memberName)) { "not a valid name: $memberName" }
+    when (value) {
+        is Class<*> -> addMember(memberName, "\$T.class", value)
+        is Enum<*> -> addMember(memberName, "\$T.\$L", value.javaClass, value.name)
+        is String -> addMember(memberName, "\$S", value)
+        is Float -> addMember(memberName, "\$Lf", value)
+        is Char -> addMember(memberName, "'\$L'", characterLiteralWithoutSingleQuotes(value))
+        else -> addMember(memberName, "\$L", value)
+    }
+}
+
+private fun characterLiteralWithoutSingleQuotes(c: Char): String? {
+    // see https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.6
+    return when (c) {
+        '\b' -> "\\b" /* \u0008: backspace (BS) */
+        '\t' -> "\\t" /* \u0009: horizontal tab (HT) */
+        '\n' -> "\\n" /* \u000a: linefeed (LF) */
+        '\u000c' -> "\\u000c" /* \u000c: form feed (FF) */
+        '\r' -> "\\r" /* \u000d: carriage return (CR) */
+        '\"' -> "\"" /* \u0022: double quote (") */
+        '\'' -> "\\'" /* \u0027: single quote (') */
+        '\\' -> "\\\\" /* \u005c: backslash (\) */
+        else -> if (isISOControl(c)) String.format("\\u%04x", c.code) else Character.toString(c)
+    }
+}
+
 internal fun TypeMirror.safeTypeName(): TypeName = if (kind == TypeKind.NONE) {
     NONE_TYPE_NAME
 } else {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
index 8d31100..cd8ddde 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.compiler.processing
 
+import androidx.room.compiler.processing.compat.XConverters.toJavac
 import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults
 import androidx.room.compiler.processing.testcode.JavaAnnotationWithEnum
 import androidx.room.compiler.processing.testcode.JavaAnnotationWithEnumArray
@@ -37,6 +38,7 @@
 import androidx.room.compiler.processing.util.typeName
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import com.squareup.javapoet.AnnotationSpec
 import com.squareup.javapoet.ClassName
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -119,6 +121,30 @@
     }
 
     @Test
+    fun annotationSpec() {
+        val source = Source.java(
+            "test.MyClass",
+            """
+            package test;
+            import androidx.room.compiler.processing.testcode.TestSuppressWarnings;
+            @TestSuppressWarnings("test")
+            public class MyClass {}
+            """.trimIndent()
+        )
+        runTest(
+            sources = listOf(source),
+        ) { invocation ->
+            val element = invocation.processingEnv.requireTypeElement("test.MyClass")
+            val annotation =
+                element.requireAnnotation(ClassName.get(TestSuppressWarnings::class.java))
+            if (!invocation.isKsp) {
+                assertThat(annotation.toAnnotationSpec())
+                    .isEqualTo(AnnotationSpec.get(annotation.toJavac()))
+            }
+        }
+    }
+
+    @Test
     fun getAnnotationsAnnotatedWith() {
         val source = Source.kotlin(
             "MyClass.kt",
diff --git a/settings.gradle b/settings.gradle
index bd19f12..c8864ff 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -785,6 +785,7 @@
 includeProject(":startup:startup-runtime-lint", [BuildType.MAIN])
 includeProject(":swiperefreshlayout:swiperefreshlayout", [BuildType.MAIN])
 includeProject(":test:ext:junit-gtest", [BuildType.MAIN])
+includeProject(":test:integration-tests:junit-gtest-test", [BuildType.MAIN])
 includeProject(":test:screenshot:screenshot")
 includeProject(":test:screenshot:screenshot-proto", "test/screenshot/screenshot/proto")
 includeProject(":test:uiautomator:uiautomator", [BuildType.MAIN])
diff --git a/test/ext/junit-gtest/build.gradle b/test/ext/junit-gtest/build.gradle
index d0a4dea..11c513e 100644
--- a/test/ext/junit-gtest/build.gradle
+++ b/test/ext/junit-gtest/build.gradle
@@ -70,6 +70,12 @@
         }
     }
     namespace "androidx.test.ext.junitgtest"
+
+    packagingOptions {
+        jniLibs {
+            excludes += "**/libc++_shared.so"
+        }
+    }
 }
 
 afterEvaluate {
diff --git a/test/ext/junit-gtest/src/main/cpp/CMakeLists.txt b/test/ext/junit-gtest/src/main/cpp/CMakeLists.txt
index cbe6cfa..1c8f7e4 100644
--- a/test/ext/junit-gtest/src/main/cpp/CMakeLists.txt
+++ b/test/ext/junit-gtest/src/main/cpp/CMakeLists.txt
@@ -26,21 +26,4 @@
         -uJava_androidx_test_ext_junitgtest_GtestRunner_initialize
         -uJava_androidx_test_ext_junitgtest_GtestRunner_run
         -uJava_androidx_test_ext_junitgtest_GtestRunner_addTest
-        )
-
-add_library(adder
-        SHARED
-        adder.cpp
-        )
-
-add_library(apptest
-        SHARED
-        app_test.cpp
-        )
-
-target_link_libraries(apptest
-        PRIVATE
-        adder
-        googletest::gtest
-        junit-gtest
         )
\ No newline at end of file
diff --git a/test/integration-tests/junit-gtest-test/OWNERS b/test/integration-tests/junit-gtest-test/OWNERS
new file mode 100644
index 0000000..5cc09b5
--- /dev/null
+++ b/test/integration-tests/junit-gtest-test/OWNERS
@@ -0,0 +1,3 @@
+fsladkey@google.com
+danalbert@google.com
+asfalcone@google.com
\ No newline at end of file
diff --git a/test/integration-tests/junit-gtest-test/build.gradle b/test/integration-tests/junit-gtest-test/build.gradle
new file mode 100644
index 0000000..c76cbce
--- /dev/null
+++ b/test/integration-tests/junit-gtest-test/build.gradle
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    api(libs.kotlinStdlib)
+    implementation(libs.junit)
+    implementation(libs.googletest)
+    implementation(project(":test:ext:junit-gtest"))
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.espressoCore)
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 18
+        externalNativeBuild {
+            cmake {
+                arguments "-DANDROID_STL=c++_shared"
+            }
+        }
+    }
+    externalNativeBuild {
+        cmake {
+            version libs.versions.cmake.get()
+            path "src/main/cpp/CMakeLists.txt"
+        }
+    }
+
+    buildFeatures {
+        prefab = true
+    }
+    namespace "androidx.test.ext.junitgtesttest"
+}
+
diff --git a/test/integration-tests/junit-gtest-test/src/androidTest/AndroidManifest.xml b/test/integration-tests/junit-gtest-test/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..4d68dc2
--- /dev/null
+++ b/test/integration-tests/junit-gtest-test/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+</manifest>
diff --git a/test/ext/junit-gtest/src/androidTest/java/androidx/test/ext/junitgtest/GtestRunnerTest.kt b/test/integration-tests/junit-gtest-test/src/androidTest/java/androidx/test/ext/junitgtesttest/GtestRunnerTest.kt
similarity index 73%
rename from test/ext/junit-gtest/src/androidTest/java/androidx/test/ext/junitgtest/GtestRunnerTest.kt
rename to test/integration-tests/junit-gtest-test/src/androidTest/java/androidx/test/ext/junitgtesttest/GtestRunnerTest.kt
index 2511fb3..ac842ad 100644
--- a/test/ext/junit-gtest/src/androidTest/java/androidx/test/ext/junitgtest/GtestRunnerTest.kt
+++ b/test/integration-tests/junit-gtest-test/src/androidTest/java/androidx/test/ext/junitgtesttest/GtestRunnerTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.test.ext.junitgtest
+package androidx.test.ext.junitgtesttest
 
+import androidx.test.ext.junitgtest.GtestRunner
+import androidx.test.ext.junitgtest.TargetLibrary
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.Description
@@ -24,6 +26,15 @@
 import org.junit.runner.notification.RunListener
 import org.junit.runner.notification.RunNotifier
 
+/**
+ * Tests the GtestRunner
+ *
+ * These specific tests would be more appropriate to put in junit-gtest itself, and have an
+ * integration test app run the tests more like an actual app consuming the library would (basically
+ * the [NativeTests] class), but due to b/236913987 it is currently difficult or impossible to
+ * make the test libraries ('apptest') available to the tests without including them in the release
+ * AAR.
+ */
 class GtestRunnerTest {
     private val runListener = CountingRunListener()
     private val runNotifier = RunNotifier().apply {
@@ -51,8 +62,8 @@
         runner.run(runNotifier)
         assertThat(runListener.descriptions.map { it.displayName }).isEqualTo(
             listOf(
-                "adder_pass(androidx.test.ext.junitgtest.GtestRunnerTest\$NativeTests)",
-                "foo_fail(androidx.test.ext.junitgtest.GtestRunnerTest\$NativeTests)")
+                "adder_pass(androidx.test.ext.junitgtesttest.GtestRunnerTest\$NativeTests)",
+                "foo_fail(androidx.test.ext.junitgtesttest.GtestRunnerTest\$NativeTests)")
 
         )
     }
diff --git a/test/integration-tests/junit-gtest-test/src/main/AndroidManifest.xml b/test/integration-tests/junit-gtest-test/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..037bd65
--- /dev/null
+++ b/test/integration-tests/junit-gtest-test/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+</manifest>
\ No newline at end of file
diff --git a/test/integration-tests/junit-gtest-test/src/main/cpp/CMakeLists.txt b/test/integration-tests/junit-gtest-test/src/main/cpp/CMakeLists.txt
new file mode 100644
index 0000000..6163455
--- /dev/null
+++ b/test/integration-tests/junit-gtest-test/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.10.2)
+
+project(junit-gtest-test LANGUAGES CXX)
+
+find_package(googletest REQUIRED CONFIG)
+find_package(junit-gtest REQUIRED CONFIG)
+
+add_library(adder
+        SHARED
+        adder.cpp
+        )
+
+add_library(apptest
+        SHARED
+        app_test.cpp
+        )
+
+target_link_libraries(apptest
+        PRIVATE
+        adder
+        googletest::gtest
+        junit-gtest::junit-gtest
+        )
\ No newline at end of file
diff --git a/test/ext/junit-gtest/src/main/cpp/adder.cpp b/test/integration-tests/junit-gtest-test/src/main/cpp/adder.cpp
similarity index 100%
rename from test/ext/junit-gtest/src/main/cpp/adder.cpp
rename to test/integration-tests/junit-gtest-test/src/main/cpp/adder.cpp
diff --git a/test/ext/junit-gtest/src/main/cpp/adder.h b/test/integration-tests/junit-gtest-test/src/main/cpp/adder.h
similarity index 100%
rename from test/ext/junit-gtest/src/main/cpp/adder.h
rename to test/integration-tests/junit-gtest-test/src/main/cpp/adder.h
diff --git a/test/ext/junit-gtest/src/main/cpp/app_test.cpp b/test/integration-tests/junit-gtest-test/src/main/cpp/app_test.cpp
similarity index 100%
rename from test/ext/junit-gtest/src/main/cpp/app_test.cpp
rename to test/integration-tests/junit-gtest-test/src/main/cpp/app_test.cpp
diff --git a/test/uiautomator/integration-tests/testapp/lint-baseline.xml b/test/uiautomator/integration-tests/testapp/lint-baseline.xml
index 0e0e653..61e4ed9 100644
--- a/test/uiautomator/integration-tests/testapp/lint-baseline.xml
+++ b/test/uiautomator/integration-tests/testapp/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.4.0-alpha01" type="baseline" client="gradle" dependencies="false" name="AGP (7.4.0-alpha01)" variant="all" version="7.4.0-alpha01">
+<issues format="6" by="lint 7.4.0-alpha05" type="baseline" client="gradle" dependencies="false" name="AGP (7.4.0-alpha05)" variant="all" version="7.4.0-alpha05">
 
     <issue
         id="DuplicateIds"
@@ -17,191 +17,4 @@
             message="Duplicate id `@+id/shared_id` originally defined here"/>
     </issue>
 
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/BySelectorTestClazzActivity.java"
-            line="27"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/BySelectorTestDescActivity.java"
-            line="29"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/BySelectorTestHasChildActivity.java"
-            line="28"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/BySelectorTestResActivity.java"
-            line="28"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/BySelectorTestTextActivity.java"
-            line="28"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/MainActivity.java"
-            line="28"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClearTextActivity.java"
-            line="27"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickActivity.java"
-            line="30"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onButtonClick(View v) {"
-        errorLine2="                              ~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickActivity.java"
-            line="36"
-            column="31"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitActivity.java"
-            line="31"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void launchNewWindow(View v) {"
-        errorLine2="                                ~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitActivity.java"
-            line="37"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitConfirmActivity.java"
-            line="28"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/UiObject2TestGetClassNameActivity.java"
-            line="28"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/UiObject2TestLongClickActivity.java"
-            line="30"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onButtonLongClick(View v) {"
-        errorLine2="                                  ~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/UiObject2TestLongClickActivity.java"
-            line="44"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/UiObject2TestPinchActivity.java"
-            line="35"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void onCreate(Bundle savedInstanceState) {"
-        errorLine2="                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/testapp/UiObject2TestVerticalScrollActivity.java"
-            line="35"
-            column="26"/>
-    </issue>
-
 </issues>
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/BySelectorTests.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/BySelectorTests.java
index 38e6ca3..6fa00bb 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/BySelectorTests.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/BySelectorTests.java
@@ -20,16 +20,9 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.ProgressBar;
 import android.widget.RadioButton;
 import android.widget.RatingBar;
-import android.widget.SeekBar;
-import android.widget.Switch;
-import android.widget.TextView;
-import android.widget.ToggleButton;
+import android.widget.VideoView;
 
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.BySelector;
@@ -37,6 +30,8 @@
 
 import org.junit.Test;
 
+import java.util.regex.Pattern;
+
 public class BySelectorTests extends BaseTest {
 
     @Test
@@ -56,129 +51,35 @@
     }
 
     @Test
-    public void testClazzButton() {
+    public void testClazz() {
         launchTestActivity(BySelectorTestClazzActivity.class);
 
-        // Button
-        assertNotNull(mDevice.findObject(By.clazz("android.widget", "Button")));
+        // Single string combining package name and class name.
         assertNotNull(mDevice.findObject(By.clazz("android.widget.Button")));
-        assertNotNull(mDevice.findObject(By.clazz(".Button")));
-        assertNotNull(mDevice.findObject(By.clazz(Button.class)));
-    }
-
-    @Test
-    public void testClazzCheckBox() {
-        launchTestActivity(BySelectorTestClazzActivity.class);
-
-        // CheckBox
-        assertNotNull(mDevice.findObject(By.clazz("android.widget", "CheckBox")));
         assertNotNull(mDevice.findObject(By.clazz("android.widget.CheckBox")));
-        assertNotNull(mDevice.findObject(By.clazz(".CheckBox")));
-        assertNotNull(mDevice.findObject(By.clazz(CheckBox.class)));
-    }
-
-    @Test
-    public void testClazzEditText() {
-        launchTestActivity(BySelectorTestClazzActivity.class);
-
-        // EditText
-        assertNotNull(mDevice.findObject(By.clazz("android.widget", "EditText")));
-        assertNotNull(mDevice.findObject(By.clazz("android.widget.EditText")));
-        assertNotNull(mDevice.findObject(By.clazz(".EditText")));
-        assertNotNull(mDevice.findObject(By.clazz(EditText.class)));
-    }
-
-    @Test
-    public void testClazzProgressBar() {
-        launchTestActivity(BySelectorTestClazzActivity.class);
-
-        // ProgressBar
-        assertNotNull(mDevice.findObject(By.clazz("android.widget", "ProgressBar")));
-        assertNotNull(mDevice.findObject(By.clazz("android.widget.ProgressBar")));
-        assertNotNull(mDevice.findObject(By.clazz(".ProgressBar")));
-        assertNotNull(mDevice.findObject(By.clazz(ProgressBar.class)));
-    }
-
-    @Test
-    public void testClazzRadioButton() {
-        launchTestActivity(BySelectorTestClazzActivity.class);
-
-        // RadioButton
-        assertNotNull(mDevice.findObject(By.clazz("android.widget", "RadioButton")));
-        assertNotNull(mDevice.findObject(By.clazz("android.widget.RadioButton")));
-        assertNotNull(mDevice.findObject(By.clazz(".RadioButton")));
-        assertNotNull(mDevice.findObject(By.clazz(RadioButton.class)));
-    }
-
-    @Test
-    public void testClazzRatingBar() {
-        launchTestActivity(BySelectorTestClazzActivity.class);
-
-        // RatingBar
-        assertNotNull(mDevice.findObject(By.clazz("android.widget", "RatingBar")));
-        assertNotNull(mDevice.findObject(By.clazz("android.widget.RatingBar")));
-        assertNotNull(mDevice.findObject(By.clazz(".RatingBar")));
-        assertNotNull(mDevice.findObject(By.clazz(RatingBar.class)));
-    }
-
-    @Test
-    public void testClazzSeekBar() {
-        launchTestActivity(BySelectorTestClazzActivity.class);
-
-        // SeekBar
-        assertNotNull(mDevice.findObject(By.clazz("android.widget", "SeekBar")));
-        assertNotNull(mDevice.findObject(By.clazz("android.widget.SeekBar")));
-        assertNotNull(mDevice.findObject(By.clazz(".SeekBar")));
-        assertNotNull(mDevice.findObject(By.clazz(SeekBar.class)));
-    }
-
-    @Test
-    public void testClazzSwitch() {
-        launchTestActivity(BySelectorTestClazzActivity.class);
-
-        // Switch
-        assertNotNull(mDevice.findObject(By.clazz("android.widget", "Switch")));
-        assertNotNull(mDevice.findObject(By.clazz("android.widget.Switch")));
-        assertNotNull(mDevice.findObject(By.clazz(".Switch")));
-        assertNotNull(mDevice.findObject(By.clazz(Switch.class)));
-    }
-
-    @Test
-    public void testClazzTextView() {
-        launchTestActivity(BySelectorTestClazzActivity.class);
-
-        // TextView
-        assertNotNull(mDevice.findObject(By.clazz("android.widget", "TextView")));
-        assertNotNull(mDevice.findObject(By.clazz("android.widget.TextView")));
-        assertNotNull(mDevice.findObject(By.clazz(".TextView")));
-        assertNotNull(mDevice.findObject(By.clazz(TextView.class)));
-    }
-
-    @Test
-    public void testClazzToggleButton() {
-        launchTestActivity(BySelectorTestClazzActivity.class);
-
-        // ToggleButton
-        assertNotNull(mDevice.findObject(By.clazz("android.widget", "ToggleButton")));
-        assertNotNull(mDevice.findObject(By.clazz("android.widget.ToggleButton")));
-        assertNotNull(mDevice.findObject(By.clazz(".ToggleButton")));
-        assertNotNull(mDevice.findObject(By.clazz(ToggleButton.class)));
-    }
-
-    @Test
-    public void testClazzNotFound() {
-        launchTestActivity(BySelectorTestClazzActivity.class);
-
-        // Non-existent class
-        assertNull(mDevice.findObject(By.clazz("android.widget", "NonExistentClass")));
         assertNull(mDevice.findObject(By.clazz("android.widget.NonExistentClass")));
-        assertNull(mDevice.findObject(By.clazz(".NonExistentClass")));
-    }
 
-    // TODO(b/235841286): Implement these for clazz():
-    // 1. Custom class
-    // 2. Patterns
-    // 3. Runtime Widgets
+        // Single string as partial class name, starting with a dot.
+        // The package will be assumed as `android.widget`.
+        assertNotNull(mDevice.findObject(By.clazz(".SeekBar")));
+        assertNotNull(mDevice.findObject(By.clazz(".Switch")));
+        assertNull(mDevice.findObject(By.clazz(".NonExistentClass")));
+
+        // Two separate strings as package name and class name.
+        assertNotNull(mDevice.findObject(By.clazz("android.widget", "EditText")));
+        assertNotNull(mDevice.findObject(By.clazz("android.widget", "ProgressBar")));
+        assertNull(mDevice.findObject(By.clazz("android.widget", "NonExistentClass")));
+
+        // Class directly.
+        assertNotNull(mDevice.findObject(By.clazz(RadioButton.class)));
+        assertNotNull(mDevice.findObject(By.clazz(RatingBar.class)));
+        assertNull(mDevice.findObject(By.clazz(VideoView.class)));
+
+        // Pattern of the class name.
+        assertNotNull(mDevice.findObject(By.clazz(Pattern.compile(".*TextView"))));
+        assertNotNull(mDevice.findObject(By.clazz(Pattern.compile(".*get\\.C.*"))));
+        assertNull(mDevice.findObject(By.clazz(Pattern.compile(".*TextView.+"))));
+    }
 
     @Test
     public void testDescSetFromResource() {
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Tests.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Tests.java
index d7cdc07..7aeb18d 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Tests.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObject2Tests.java
@@ -24,7 +24,6 @@
 
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.os.SystemClock;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.uiautomator.By;
@@ -37,9 +36,10 @@
 import java.util.List;
 
 public class UiObject2Tests extends BaseTest {
+    private static final int TIMEOUT_MS = 10_000;
 
     @Test
-    public void testClearTextField() {
+    public void testClear() {
         launchTestActivity(UiObject2TestClearTextActivity.class);
 
         UiObject2 object = mDevice.findObject(By.res(TEST_APP, "edit_text"));
@@ -51,23 +51,17 @@
     }
 
     @Test
-    public void testClickButton() {
+    public void testClick() {
         launchTestActivity(UiObject2TestClickActivity.class);
 
         // Find the button and verify its initial state
         UiObject2 button = mDevice.findObject(By.res(TEST_APP, "button"));
         assertEquals("Click Me!", button.getText());
-        SystemClock.sleep(1000);
 
         // Click on the button and verify that the text has changed
         button.click();
-        button.wait(Until.textEquals("I've been clicked!"), 10000);
+        button.wait(Until.textEquals("I've been clicked!"), TIMEOUT_MS);
         assertEquals("I've been clicked!", button.getText());
-    }
-
-    @Test
-    public void testClickCheckBox() {
-        launchTestActivity(UiObject2TestClickActivity.class);
 
         // Find the checkbox and verify its initial state
         UiObject2 checkbox = mDevice.findObject(By.res(TEST_APP, "check_box"));
@@ -75,17 +69,17 @@
 
         // Click on the checkbox and verify that it is now checked
         checkbox.click();
-        checkbox.wait(Until.checked(true), 10000);
+        checkbox.wait(Until.checked(true), TIMEOUT_MS);
         assertTrue(checkbox.isChecked());
     }
 
     @Test
-    public void testClickAndWaitForNewWindow() {
+    public void testClickAndWait() {
         launchTestActivity(UiObject2TestClickAndWaitActivity.class);
 
         // Click the button and wait for a new window
         UiObject2 button = mDevice.findObject(By.res(TEST_APP, "new_window_button"));
-        button.clickAndWait(Until.newWindow(), 5000);
+        assertTrue(button.clickAndWait(Until.newWindow(), TIMEOUT_MS));
     }
 
     @Test
@@ -150,83 +144,14 @@
     }
 
     @Test
-    public void testGetClassNameButton() {
+    public void testGetClassName() {
         launchTestActivity(UiObject2TestGetClassNameActivity.class);
 
-        UiObject2 object = mDevice.findObject(By.res(TEST_APP, "button"));
-        assertEquals("android.widget.Button", object.getClassName());
-    }
+        UiObject2 button = mDevice.findObject(By.res(TEST_APP, "button"));
+        assertEquals("android.widget.Button", button.getClassName());
 
-    @Test
-    public void testGetClassNameCheckBox() {
-        launchTestActivity(UiObject2TestGetClassNameActivity.class);
-
-        UiObject2 object = mDevice.findObject(By.res(TEST_APP, "check_box"));
-        assertEquals("android.widget.CheckBox", object.getClassName());
-    }
-
-    @Test
-    public void testGetClassNameEditText() {
-        launchTestActivity(UiObject2TestGetClassNameActivity.class);
-
-        UiObject2 object = mDevice.findObject(By.res(TEST_APP, "edit_text"));
-        assertEquals("android.widget.EditText", object.getClassName());
-    }
-
-    @Test
-    public void testGetClassNameProgressBar() {
-        launchTestActivity(UiObject2TestGetClassNameActivity.class);
-
-        UiObject2 object = mDevice.findObject(By.res(TEST_APP, "progress_bar"));
-        assertEquals("android.widget.ProgressBar", object.getClassName());
-    }
-
-    @Test
-    public void testGetClassNameRadioButton() {
-        launchTestActivity(UiObject2TestGetClassNameActivity.class);
-
-        UiObject2 object = mDevice.findObject(By.res(TEST_APP, "radio_button"));
-        assertEquals("android.widget.RadioButton", object.getClassName());
-    }
-
-    @Test
-    public void testGetClassNameRatingBar() {
-        launchTestActivity(UiObject2TestGetClassNameActivity.class);
-
-        UiObject2 object = mDevice.findObject(By.res(TEST_APP, "rating_bar"));
-        assertEquals("android.widget.RatingBar", object.getClassName());
-    }
-
-    @Test
-    public void testGetClassNameSeekBar() {
-        launchTestActivity(UiObject2TestGetClassNameActivity.class);
-
-        UiObject2 object = mDevice.findObject(By.res(TEST_APP, "seek_bar"));
-        assertEquals("android.widget.SeekBar", object.getClassName());
-    }
-
-    @Test
-    public void testGetClassNameSwitch() {
-        launchTestActivity(UiObject2TestGetClassNameActivity.class);
-
-        UiObject2 object = mDevice.findObject(By.res(TEST_APP, "switch_toggle"));
-        assertEquals("android.widget.Switch", object.getClassName());
-    }
-
-    @Test
-    public void testGetClassNameTextView() {
-        launchTestActivity(UiObject2TestGetClassNameActivity.class);
-
-        UiObject2 object = mDevice.findObject(By.res(TEST_APP, "text_view"));
-        assertEquals("android.widget.TextView", object.getClassName());
-    }
-
-    @Test
-    public void testGetClassNameToggleButton() {
-        launchTestActivity(UiObject2TestGetClassNameActivity.class);
-
-        UiObject2 object = mDevice.findObject(By.res(TEST_APP, "toggle_button"));
-        assertEquals("android.widget.ToggleButton", object.getClassName());
+        UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "text_view"));
+        assertEquals("android.widget.TextView", textView.getClassName());
     }
 
     @Test
@@ -323,6 +248,106 @@
     }
 
     @Test
+    public void testIsCheckable() {
+        launchTestActivity(UiObject2TestClickActivity.class);
+
+        // CheckBox objects are checkable by default.
+        UiObject2 checkBox = mDevice.findObject(By.res(TEST_APP, "check_box"));
+        assertTrue(checkBox.isCheckable());
+        // Button objects are not checkable by default.
+        UiObject2 button = mDevice.findObject(By.res(TEST_APP, "button"));
+        assertFalse(button.isCheckable());
+    }
+
+    @Test
+    public void testIsChecked() {
+        launchTestActivity(UiObject2TestClickActivity.class);
+
+        UiObject2 checkBox = mDevice.findObject(By.res(TEST_APP, "check_box"));
+        assertFalse(checkBox.isChecked());
+        checkBox.click();
+        assertTrue(checkBox.isChecked());
+    }
+
+    @Test
+    public void testIsClickable() {
+        launchTestActivity(MainActivity.class);
+
+        // TextView objects are not clickable by default.
+        UiObject2 textView = mDevice.findObject(By.text("Sample text"));
+        assertFalse(textView.isClickable());
+        // Button objects are clickable by default.
+        UiObject2 button = mDevice.findObject(By.text("Accessible button"));
+        assertTrue(button.isClickable());
+    }
+
+    @Test
+    public void testIsEnabled() {
+        launchTestActivity(UiObject2TestIsEnabledActivity.class);
+
+        UiObject2 disabledObject = mDevice.findObject(By.res(TEST_APP, "disabled_text_view"));
+        assertFalse(disabledObject.isEnabled());
+        UiObject2 enabledObject = mDevice.findObject(By.res(TEST_APP, "enabled_text_view"));
+        assertTrue(enabledObject.isEnabled());
+    }
+
+    @Test
+    public void testIsFocusable() {
+        launchTestActivity(UiObject2TestIsFocusedActivity.class);
+
+        UiObject2 nonFocusableTextView = mDevice.findObject(By.res(TEST_APP,
+                "non_focusable_text_view"));
+        assertFalse(nonFocusableTextView.isFocusable());
+        UiObject2 focusableTextView = mDevice.findObject(By.res(TEST_APP, "focusable_text_view"));
+        assertTrue(focusableTextView.isFocusable());
+    }
+
+    @Test
+    public void testIsFocused() {
+        launchTestActivity(UiObject2TestIsFocusedActivity.class);
+
+        UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "focusable_text_view"));
+        assertFalse(textView.isFocused());
+        UiObject2 button = mDevice.findObject(By.res(TEST_APP, "button"));
+        button.click();
+        assertTrue(textView.isFocused());
+    }
+
+    @Test
+    public void testIsLongClickable() {
+        launchTestActivity(UiObject2TestIsLongClickableActivity.class);
+
+        UiObject2 longClickableButton = mDevice.findObject(By.res(TEST_APP,
+                "long_clickable_button"));
+        assertTrue(longClickableButton.isLongClickable());
+        UiObject2 nonLongClickableButton = mDevice.findObject(By.res(TEST_APP,
+                "non_long_clickable_button"));
+        assertFalse(nonLongClickableButton.isLongClickable());
+    }
+
+    @Test
+    public void testIsScrollable() {
+        launchTestActivity(UiObject2TestVerticalScrollActivity.class);
+
+        // ScrollView objects are scrollable by default.
+        UiObject2 scrollView = mDevice.findObject(By.res(TEST_APP, "scroll_view"));
+        assertTrue(scrollView.isScrollable());
+        // TextView objects are not scrollable by default.
+        UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "top_text"));
+        assertFalse(textView.isScrollable());
+    }
+
+    @Test
+    public void testIsSelected() {
+        launchTestActivity(UiObject2TestIsSelectedActivity.class);
+
+        UiObject2 button = mDevice.findObject(By.res(TEST_APP, "selected_button"));
+        button.click();
+        UiObject2 textView = mDevice.findObject(By.res(TEST_APP, "selected_target"));
+        assertTrue(textView.isSelected());
+    }
+
+    @Test
     public void testLongClickButton() {
         launchTestActivity(UiObject2TestLongClickActivity.class);
 
@@ -332,7 +357,7 @@
 
         // Click on the button and verify that the text has changed
         button.longClick();
-        button.wait(Until.textEquals("I've been long clicked!"), 10000);
+        button.wait(Until.textEquals("I've been long clicked!"), TIMEOUT_MS);
         assertEquals("I've been long clicked!", button.getText());
     }
 
@@ -435,7 +460,7 @@
 
         // Scroll as much as we can
         UiObject2 scrollView = mDevice.findObject(By.res(TEST_APP, "scroll_view"));
-        scrollView.wait(Until.scrollable(true), 5000);
+        scrollView.wait(Until.scrollable(true), TIMEOUT_MS);
         while (scrollView.scroll(Direction.DOWN, 1.0f)) {
             // Continue until bottom.
         }
@@ -444,37 +469,27 @@
         assertNotNull(mDevice.findObject(By.res(TEST_APP, "bottom_text")));
     }
 
+    @Test
+    public void testSetText() {
+        launchTestActivity(UiObject2TestClearTextActivity.class);
+
+        UiObject2 object = mDevice.findObject(By.res(TEST_APP, "edit_text"));
+        // Verify the text field has "sample_text" before setText()
+        assertEquals("sample_text", object.getText());
+        object.setText("new_text");
+        // Verify the text field has "new_text" after setText()
+        assertEquals("new_text", object.getText());
+    }
+
     /* TODO(b/235841473): Implement these tests
-    public void testSetText() {}
-
-    public void testWaitForExists() {}
-
-    public void testWaitForGone() {}
-    */
-
-    /* TODO(b/235841473): Implement more tests
     public void testDrag() {}
 
     public void testEquals() {}
 
     public void testFling() {}
 
-    public void testIsCheckable() {}
+    public void testWaitForExists() {}
 
-    public void testIsChecked() {}
-
-    public void testIsClickable() {}
-
-    public void testIsEnabled() {}
-
-    public void testIsFocusable() {}
-
-    public void testIsFocused() {}
-
-    public void testIsLongClickable() {}
-
-    public void testIsScrollable() {}
-
-    public void testIsSelected() {}
+    public void testWaitForGone() {}
     */
 }
diff --git a/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml b/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
index 5e6a99e..308294a 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -90,6 +90,34 @@
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
         </activity>
+        <activity android:name=".UiObject2TestIsEnabledActivity"
+            android:exported="true"
+            android:theme="@android:style/Theme.Holo.NoActionBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".UiObject2TestIsFocusedActivity"
+            android:exported="true"
+            android:theme="@android:style/Theme.Holo.NoActionBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".UiObject2TestIsLongClickableActivity"
+            android:exported="true"
+            android:theme="@android:style/Theme.Holo.NoActionBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".UiObject2TestIsSelectedActivity"
+            android:exported="true"
+            android:theme="@android:style/Theme.Holo.NoActionBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
         <activity android:name=".UiObject2TestLongClickActivity"
             android:exported="true"
             android:theme="@android:style/Theme.Holo.NoActionBar">
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestClazzActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestClazzActivity.java
index 22fdb27..c0ed81e 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestClazzActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestClazzActivity.java
@@ -19,12 +19,11 @@
 import android.app.Activity;
 import android.os.Bundle;
 
+import androidx.annotation.Nullable;
+
 public class BySelectorTestClazzActivity extends Activity {
-
-    private static final String TAG = BySelectorTestClazzActivity.class.getSimpleName();
-
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.byselector_testclazz_activity);
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestDescActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestDescActivity.java
index 42505b9..9b559c5 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestDescActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestDescActivity.java
@@ -18,15 +18,13 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.util.Log;
 import android.widget.Button;
 
+import androidx.annotation.Nullable;
+
 public class BySelectorTestDescActivity extends Activity {
-
-    private static final String TAG = BySelectorTestDescActivity.class.getSimpleName();
-
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.byselector_testdesc_activity);
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestHasChildActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestHasChildActivity.java
index 7531ba7..05f04cd 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestHasChildActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestHasChildActivity.java
@@ -18,14 +18,12 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.util.Log;
+
+import androidx.annotation.Nullable;
 
 public class BySelectorTestHasChildActivity extends Activity {
-
-    private static final String TAG = BySelectorTestHasChildActivity.class.getSimpleName();
-
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.byselector_testhaschild_activity);
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestResActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestResActivity.java
index f69a24b..fbf6c0a 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestResActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestResActivity.java
@@ -18,14 +18,12 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.util.Log;
+
+import androidx.annotation.Nullable;
 
 public class BySelectorTestResActivity extends Activity {
-
-    private static final String TAG = BySelectorTestResActivity.class.getSimpleName();
-
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.byselector_testres_activity);
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestTextActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestTextActivity.java
index 0202084..34c0c53 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestTextActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/BySelectorTestTextActivity.java
@@ -18,14 +18,12 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.util.Log;
+
+import androidx.annotation.Nullable;
 
 public class BySelectorTestTextActivity extends Activity {
-
-    private static final String TAG = BySelectorTestTextActivity.class.getSimpleName();
-
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.byselector_testtext_activity);
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/MainActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/MainActivity.java
index f0693e3..e9029c1 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/MainActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/MainActivity.java
@@ -18,14 +18,12 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.util.Log;
+
+import androidx.annotation.Nullable;
 
 public class MainActivity extends Activity {
-
-    private static final String TAG = MainActivity.class.getSimpleName();
-
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.main_activity);
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClearTextActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClearTextActivity.java
index 0a90537..6a816ba 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClearTextActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClearTextActivity.java
@@ -19,12 +19,12 @@
 import android.app.Activity;
 import android.os.Bundle;
 
+import androidx.annotation.Nullable;
+
 public class UiObject2TestClearTextActivity extends Activity {
 
-    private static final String TAG = UiObject2TestClearTextActivity.class.getSimpleName();
-
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.uiobject2_testcleartext_activity);
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickActivity.java
index d644fa4..ec0e5e1 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickActivity.java
@@ -18,22 +18,22 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.util.Log;
 import android.view.View;
 import android.widget.Button;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 public class UiObject2TestClickActivity extends Activity {
 
-    private static final String TAG = UiObject2TestClickActivity.class.getSimpleName();
-
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.uiobject2_testclick_activity);
     }
 
-    public void onButtonClick(View v) {
+    public void onButtonClick(@NonNull View v) {
         ((Button)v).setText("I've been clicked!");
     }
 }
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitActivity.java
index c14404f..79e46dd 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitActivity.java
@@ -19,22 +19,21 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
-import android.util.Log;
 import android.view.View;
-import android.widget.Button;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 public class UiObject2TestClickAndWaitActivity extends Activity {
 
-    private static final String TAG = UiObject2TestClickActivity.class.getSimpleName();
-
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.uiobject2_testclickandwait_activity);
     }
 
-    public void launchNewWindow(View v) {
+    public void launchNewWindow(@NonNull View v) {
         Intent intent = new Intent(this, UiObject2TestClickAndWaitConfirmActivity.class);
         startActivity(intent);
     }
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitConfirmActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitConfirmActivity.java
index b181d83..72fd28a 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitConfirmActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestClickAndWaitConfirmActivity.java
@@ -18,14 +18,13 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.util.Log;
+
+import androidx.annotation.Nullable;
 
 public class UiObject2TestClickAndWaitConfirmActivity extends Activity {
 
-    private static final String TAG = UiObject2TestClickActivity.class.getSimpleName();
-
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.uiobject2_testclickandwaitconfirm_activity);
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestGetClassNameActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestGetClassNameActivity.java
index c6a6d70..6777abc 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestGetClassNameActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestGetClassNameActivity.java
@@ -18,14 +18,13 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.util.Log;
+
+import androidx.annotation.Nullable;
 
 public class UiObject2TestGetClassNameActivity extends Activity {
 
-    private static final String TAG = UiObject2TestGetClassNameActivity.class.getSimpleName();
-
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         // Reuse layout from BySelectorTestClazz
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestIsEnabledActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestIsEnabledActivity.java
new file mode 100644
index 0000000..e5059b2
--- /dev/null
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestIsEnabledActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 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.test.uiautomator.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+public class UiObject2TestIsEnabledActivity extends Activity {
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.uiobject2_testisenabled_activity);
+    }
+}
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestIsFocusedActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestIsFocusedActivity.java
new file mode 100644
index 0000000..a2bbb18
--- /dev/null
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestIsFocusedActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 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.test.uiautomator.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+public class UiObject2TestIsFocusedActivity extends Activity {
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.uiobject2_testisfocused_activity);
+
+        Button focusedButton = (Button) findViewById(R.id.button);
+        TextView focusedTarget = (TextView) findViewById(R.id.focusable_text_view);
+        focusedButton.setOnClickListener(v -> focusedTarget.requestFocus());
+    }
+}
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestIsLongClickableActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestIsLongClickableActivity.java
new file mode 100644
index 0000000..6684417
--- /dev/null
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestIsLongClickableActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 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.test.uiautomator.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+public class UiObject2TestIsLongClickableActivity extends Activity {
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.uiobject2_testislongclickable_activity);
+    }
+}
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestIsSelectedActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestIsSelectedActivity.java
new file mode 100644
index 0000000..fc468af
--- /dev/null
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestIsSelectedActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 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.test.uiautomator.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+public class UiObject2TestIsSelectedActivity extends Activity {
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.uiobject2_testisselected_activity);
+
+        Button selectedButton = (Button) findViewById(R.id.selected_button);
+        TextView selectedTarget = (TextView) findViewById(R.id.selected_target);
+        selectedButton.setOnClickListener(v -> selectedTarget.setSelected(true));
+    }
+}
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestLongClickActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestLongClickActivity.java
index 91ff485..30d3627 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestLongClickActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestLongClickActivity.java
@@ -18,30 +18,25 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.util.Log;
 import android.view.View;
 import android.widget.Button;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 public class UiObject2TestLongClickActivity extends Activity {
 
-    private static final String TAG = UiObject2TestLongClickActivity.class.getSimpleName();
-
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.uiobject2_testlongclick_activity);
 
         Button button = (Button)findViewById(R.id.button);
-        button.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                onButtonLongClick(v);
-            }
-        });
+        button.setOnClickListener(this::onButtonLongClick);
     }
 
-    public void onButtonLongClick(View v) {
+    public void onButtonLongClick(@NonNull View v) {
         ((Button)v).setText("I've been long clicked!");
     }
 }
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestPinchActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestPinchActivity.java
index d4a729e..d205048 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestPinchActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestPinchActivity.java
@@ -22,22 +22,20 @@
 import android.view.MotionEvent;
 import android.view.ScaleGestureDetector;
 import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
-import android.view.View;
 import android.widget.TextView;
 
-public class UiObject2TestPinchActivity extends Activity {
+import androidx.annotation.Nullable;
 
-    private static final String TAG = UiObject2TestPinchActivity.class.getSimpleName();
+public class UiObject2TestPinchActivity extends Activity {
 
     private ScaleGestureDetector mScaleDetector;
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.uiobject2_testpinch_activity);
 
-        //LinearLayout pinchArea = (LinearLayout)findViewById(R.id.pinch_area);
         final TextView scaleFactor = (TextView)findViewById(R.id.scale_factor);
 
         mScaleDetector = new ScaleGestureDetector(this, new SimpleOnScaleGestureListener() {
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestVerticalScrollActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestVerticalScrollActivity.java
index 59c3ee2..12bd767 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestVerticalScrollActivity.java
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/UiObject2TestVerticalScrollActivity.java
@@ -17,7 +17,6 @@
 package androidx.test.uiautomator.testapp;
 
 import android.app.Activity;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.GestureDetector;
@@ -25,26 +24,18 @@
 import android.view.MotionEvent;
 import android.widget.TextView;
 
-public class UiObject2TestVerticalScrollActivity extends Activity {
+import androidx.annotation.Nullable;
 
-    private static final String TAG = UiObject2TestVerticalScrollActivity.class.getSimpleName();
+public class UiObject2TestVerticalScrollActivity extends Activity {
 
     private GestureDetector mGestureDetector;
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.uiobject2_testverticalscroll_activity);
 
-        /*
-        final TextView topText = (TextView)findViewById(R.id.top_text);
-        Log.d("FOO", String.format("top_text: %s, %s, %s, %s", topText.getLeft(), topText.getTop(), topText.getRight(), topText.getBottom()));
-        final TextView fromTop5000 = (TextView)findViewById(R.id.from_top_5000);
-        Log.d("FOO", String.format("from_top_5000: %s, %s, %s, %s", fromTop5000.getLeft(), fromTop5000.getTop(), fromTop5000.getRight(), fromTop5000.getBottom()));
-        final TextView fromTop10000 = (TextView)findViewById(R.id.from_top_10000);
-        final TextView fromTop15000 = (TextView)findViewById(R.id.from_top_15000);
-*/
         final TextView flingDetected = (TextView)findViewById(R.id.fling_detected);
 
         mGestureDetector = new GestureDetector(this, new SimpleOnGestureListener() {
diff --git a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testcleartext_activity.xml b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testcleartext_activity.xml
index 191b446..ad5651e 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testcleartext_activity.xml
+++ b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testcleartext_activity.xml
@@ -27,4 +27,5 @@
             android:layout_height="wrap_content"
             android:inputType="text"
             android:text="sample_text" />
+
 </LinearLayout>
\ No newline at end of file
diff --git a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testisenabled_activity.xml b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testisenabled_activity.xml
new file mode 100644
index 0000000..04db32d
--- /dev/null
+++ b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testisenabled_activity.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        tools:context=".UiObject2TestIsEnabledActivity">
+
+    <TextView
+            android:id="@+id/disabled_text_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="Disabled Text View" />
+
+    <TextView
+            android:id="@+id/enabled_text_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:enabled="true"
+            android:text="Enabled Text View" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testisfocused_activity.xml b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testisfocused_activity.xml
new file mode 100644
index 0000000..ddc1d51
--- /dev/null
+++ b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testisfocused_activity.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        tools:context=".UiObject2TestIsFocusedActivity">
+
+    <Button
+            android:id="@+id/button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="button" />
+
+    <TextView
+            android:id="@+id/focusable_text_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:focusable="true"
+            android:focusableInTouchMode="true"
+            android:text="focusable_text_view" />
+
+    <TextView
+            android:id="@+id/non_focusable_text_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:focusable="false"
+            android:focusableInTouchMode="false"
+            android:text="non_focusable_text_view" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testislongclickable_activity.xml b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testislongclickable_activity.xml
new file mode 100644
index 0000000..25f053d
--- /dev/null
+++ b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testislongclickable_activity.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        tools:context=".UiObject2TestIsLongClickableActivity">
+
+    <Button android:id="@+id/long_clickable_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:longClickable="true"
+            android:text="long_clickable_button" />
+
+    <Button android:id="@+id/non_long_clickable_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="non_long_clickable_button" />
+
+</LinearLayout>
diff --git a/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testisselected_activity.xml b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testisselected_activity.xml
new file mode 100644
index 0000000..e4d6997
--- /dev/null
+++ b/test/uiautomator/integration-tests/testapp/src/main/res/layout/uiobject2_testisselected_activity.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        tools:context=".UiObject2TestIsSelectedActivity">
+
+    <Button android:id="@+id/selected_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="selected_button" />
+    <TextView android:id="@+id/selected_target"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="selected_target" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tracing/tracing-perfetto-binary/api/current.txt b/tracing/tracing-perfetto-binary/api/current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/tracing/tracing-perfetto-binary/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/tracing/tracing-perfetto-binary/api/public_plus_experimental_current.txt b/tracing/tracing-perfetto-binary/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/tracing/tracing-perfetto-binary/api/public_plus_experimental_current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/tracing/tracing-perfetto-binary/api/res-current.txt b/tracing/tracing-perfetto-binary/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tracing/tracing-perfetto-binary/api/res-current.txt
diff --git a/tracing/tracing-perfetto-binary/api/restricted_current.txt b/tracing/tracing-perfetto-binary/api/restricted_current.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/tracing/tracing-perfetto-binary/api/restricted_current.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/tracing/tracing-perfetto-binary/build.gradle b/tracing/tracing-perfetto-binary/build.gradle
index cf69010..11f4655 100644
--- a/tracing/tracing-perfetto-binary/build.gradle
+++ b/tracing/tracing-perfetto-binary/build.gradle
@@ -82,7 +82,7 @@
 
 androidx {
     name = "AndroidX Tracing: Perfetto SDK Native Dependencies"
-    publish = Publish.SNAPSHOT_ONLY
+    publish = Publish.SNAPSHOT_AND_RELEASE
     mavenVersion = LibraryVersions.TRACING_PERFETTO
     mavenGroup = LibraryGroups.TRACING
     inceptionYear = "2022"
diff --git a/tracing/tracing-perfetto/api/current.txt b/tracing/tracing-perfetto/api/current.txt
new file mode 100644
index 0000000..4d8a122
--- /dev/null
+++ b/tracing/tracing-perfetto/api/current.txt
@@ -0,0 +1,13 @@
+// Signature format: 4.0
+package androidx.tracing.perfetto {
+
+  public final class Tracing {
+    method public boolean isEnabled();
+    method public void traceEventEnd();
+    method public void traceEventStart(int key, String traceInfo);
+    property public final boolean isEnabled;
+    field public static final androidx.tracing.perfetto.Tracing INSTANCE;
+  }
+
+}
+
diff --git a/tracing/tracing-perfetto/api/public_plus_experimental_current.txt b/tracing/tracing-perfetto/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..4d8a122
--- /dev/null
+++ b/tracing/tracing-perfetto/api/public_plus_experimental_current.txt
@@ -0,0 +1,13 @@
+// Signature format: 4.0
+package androidx.tracing.perfetto {
+
+  public final class Tracing {
+    method public boolean isEnabled();
+    method public void traceEventEnd();
+    method public void traceEventStart(int key, String traceInfo);
+    property public final boolean isEnabled;
+    field public static final androidx.tracing.perfetto.Tracing INSTANCE;
+  }
+
+}
+
diff --git a/tracing/tracing-perfetto/api/res-current.txt b/tracing/tracing-perfetto/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tracing/tracing-perfetto/api/res-current.txt
diff --git a/tracing/tracing-perfetto/api/restricted_current.txt b/tracing/tracing-perfetto/api/restricted_current.txt
new file mode 100644
index 0000000..4d8a122
--- /dev/null
+++ b/tracing/tracing-perfetto/api/restricted_current.txt
@@ -0,0 +1,13 @@
+// Signature format: 4.0
+package androidx.tracing.perfetto {
+
+  public final class Tracing {
+    method public boolean isEnabled();
+    method public void traceEventEnd();
+    method public void traceEventStart(int key, String traceInfo);
+    property public final boolean isEnabled;
+    field public static final androidx.tracing.perfetto.Tracing INSTANCE;
+  }
+
+}
+
diff --git a/tracing/tracing-perfetto/build.gradle b/tracing/tracing-perfetto/build.gradle
index 785940a..d2296db 100644
--- a/tracing/tracing-perfetto/build.gradle
+++ b/tracing/tracing-perfetto/build.gradle
@@ -34,7 +34,6 @@
 
 androidx {
     name = "AndroidX Tracing: Perfetto SDK"
-    runApiTasks= new RunApiTasks.No("The API is still evolving")
     publish = Publish.SNAPSHOT_AND_RELEASE
     mavenVersion = LibraryVersions.TRACING_PERFETTO
     mavenGroup = LibraryGroups.TRACING
diff --git a/tracing/tracing-perfetto/src/androidTest/java/androidx/tracing/perfetto/test/TracingTest.kt b/tracing/tracing-perfetto/src/androidTest/java/androidx/tracing/perfetto/test/TracingTest.kt
index 05d31ff..09312f3 100644
--- a/tracing/tracing-perfetto/src/androidTest/java/androidx/tracing/perfetto/test/TracingTest.kt
+++ b/tracing/tracing-perfetto/src/androidTest/java/androidx/tracing/perfetto/test/TracingTest.kt
@@ -50,4 +50,4 @@
 
         // TODO(214562374): verify the content by getting it back from Perfetto
     }
-}
\ No newline at end of file
+}
diff --git a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/Tracing.kt b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/Tracing.kt
index 453fcc0..6a4b453 100644
--- a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/Tracing.kt
+++ b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/Tracing.kt
@@ -40,13 +40,12 @@
 
 object Tracing {
     /**
-     * Indicates that the tracing library has been loaded and that the app was registered with
-     * Perfetto as a data source.
-     *
-     * Note: some of class' code relies on the field ever changing from true -> false and not in
-     * the other direction, which is realistic (we cannot unload the library or unregister with
-     * Perfetto at the time of writing).
+     * Indicates whether the tracing library has been loaded and the app registered with
+     * Perfetto SDK.
      */
+    // Note: some of class' code relies on the field never changing from true -> false,
+    // which is realistic (at the time of writing this, we are unable to unload the library and
+    // unregister the app with Perfetto).
     var isEnabled: Boolean = false
         private set
 
@@ -59,7 +58,7 @@
     private val enableTracingLock = ReentrantReadWriteLock()
 
     @RequiresApi(Build.VERSION_CODES.R) // TODO(234351579): Support API < 30
-    fun enable() = enable(null)
+    internal fun enable() = enable(null)
 
     @RequiresApi(Build.VERSION_CODES.R) // TODO(234351579): Support API < 30
     internal fun enable(file: File, context: Context) = enable(file to context)
@@ -95,17 +94,17 @@
                 is IncorrectChecksumException, is UnapprovedLocationException ->
                     EnableTracingResponse(
                         exitCode = RESULT_CODE_ERROR_BINARY_VERIFICATION_ERROR,
-                        errorMessage = t.toErrorMessage()
+                        errorMessage = errorMessage(t)
                     )
                 is UnsatisfiedLinkError ->
                     EnableTracingResponse(
                         exitCode = RESULT_CODE_ERROR_BINARY_MISSING,
-                        errorMessage = t.toErrorMessage()
+                        errorMessage = errorMessage(t)
                     )
                 is Exception ->
                     EnableTracingResponse(
                         exitCode = RESULT_CODE_ERROR_OTHER,
-                        errorMessage = t.toErrorMessage()
+                        errorMessage = errorMessage(t)
                     )
                 else -> throw t
             }
@@ -128,7 +127,7 @@
         try {
             PerfettoNative.nativeRegisterWithPerfetto()
         } catch (e: Exception) {
-            return EnableTracingResponse(RESULT_CODE_ERROR_OTHER, errorMessage = e.toErrorMessage())
+            return EnableTracingResponse(RESULT_CODE_ERROR_OTHER, errorMessage = errorMessage(e))
         }
 
         isEnabled = true
@@ -136,27 +135,29 @@
     }
 
     // TODO: remove and replace with an observer wired into Perfetto
-    fun flushEvents() {
+    internal fun flushEvents() {
         if (isEnabled) {
             PerfettoNative.nativeFlushEvents()
         }
     }
 
+    /** Writes a trace message to indicate that a given section of code has begun. */
     fun traceEventStart(key: Int, traceInfo: String) {
         if (isEnabled) {
             PerfettoNative.nativeTraceEventBegin(key, traceInfo)
         }
     }
 
+    /** Writes a trace message to indicate that a given section of code has ended. */
     fun traceEventEnd() {
         if (isEnabled) PerfettoNative.nativeTraceEventEnd()
     }
 
-    data class EnableTracingResponse(
+    internal data class EnableTracingResponse(
         @EnableTracingResultCode val exitCode: Int,
         val errorMessage: String? = null
     ) {
-        fun toJsonString(): String {
+        internal fun toJsonString(): String {
             val output = StringWriter()
 
             JsonWriter(output).use {
@@ -179,7 +180,7 @@
             return output.toString()
         }
     }
-}
 
-internal fun Throwable.toErrorMessage(): String =
-    javaClass.name + if (message != null) ": $message" else ""
+    internal fun errorMessage(t: Throwable): String =
+        t.javaClass.name + if (t.message != null) ": ${t.message}" else ""
+}
diff --git a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/TracingReceiver.kt b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/TracingReceiver.kt
index a7d4ab1..1c0f0db9 100644
--- a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/TracingReceiver.kt
+++ b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/TracingReceiver.kt
@@ -24,6 +24,7 @@
 import androidx.annotation.RestrictTo
 import androidx.annotation.RestrictTo.Scope.LIBRARY
 import androidx.tracing.perfetto.Tracing.EnableTracingResponse
+import androidx.tracing.perfetto.Tracing.errorMessage
 import androidx.tracing.perfetto.TracingReceiver.Companion.ACTION_ENABLE_TRACING
 import java.io.File
 import java.util.concurrent.LinkedBlockingQueue
@@ -127,7 +128,7 @@
                 } catch (e: Exception) {
                     EnableTracingResponse(
                         exitCode = RESULT_CODE_ERROR_OTHER,
-                        errorMessage = e.toErrorMessage()
+                        errorMessage = errorMessage(e)
                     )
                 }
             }
diff --git a/vectordrawable/integration-tests/testapp/src/main/java/com/example/android/support/vectordrawable/app/AVDCListenerDemo.java b/vectordrawable/integration-tests/testapp/src/main/java/com/example/android/support/vectordrawable/app/AVDCListenerDemo.java
index 37a162b..1b6cba4 100644
--- a/vectordrawable/integration-tests/testapp/src/main/java/com/example/android/support/vectordrawable/app/AVDCListenerDemo.java
+++ b/vectordrawable/integration-tests/testapp/src/main/java/com/example/android/support/vectordrawable/app/AVDCListenerDemo.java
@@ -23,6 +23,7 @@
 import android.view.View;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.widget.AppCompatImageView;
 import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
@@ -57,24 +58,24 @@
         Animatable2Compat.AnimationCallback textView1Callback = new
                 Animatable2Compat.AnimationCallback() {
                     @Override
-                    public void onAnimationStart(Drawable drawable) {
+                    public void onAnimationStart(@NonNull Drawable drawable) {
                         textView1.setText("AVD 1 started");
                     }
 
                     @Override
-                    public void onAnimationEnd(Drawable drawable) {
+                    public void onAnimationEnd(@NonNull Drawable drawable) {
                         textView1.setText("AVD 1 Ended");
                     }
                 };
         Animatable2Compat.AnimationCallback textView2Callback = new
                 Animatable2Compat.AnimationCallback() {
                     @Override
-                    public void onAnimationStart(Drawable drawable) {
+                    public void onAnimationStart(@NonNull Drawable drawable) {
                         textView2.setText("AVD 1 started");
                     }
 
                     @Override
-                    public void onAnimationEnd(Drawable drawable) {
+                    public void onAnimationEnd(@NonNull Drawable drawable) {
                         textView2.setText("AVD 1 Ended");
                     }
                 };
@@ -86,12 +87,12 @@
         AnimatedVectorDrawableCompat.registerAnimationCallback(drawable2,
                 new Animatable2Compat.AnimationCallback() {
                     @Override
-                    public void onAnimationStart(Drawable drawable) {
+                    public void onAnimationStart(@NonNull Drawable drawable) {
                         textView3.setText("AVD 2 started");
                     }
 
                     @Override
-                    public void onAnimationEnd(Drawable drawable) {
+                    public void onAnimationEnd(@NonNull Drawable drawable) {
                         textView3.setText("AVD 2 Ended");
                     }
                 });
@@ -99,12 +100,12 @@
         Animatable2Compat.AnimationCallback textView4Callback = new
                 Animatable2Compat.AnimationCallback() {
                     @Override
-                    public void onAnimationStart(Drawable drawable) {
+                    public void onAnimationStart(@NonNull Drawable drawable) {
                         textView4.setText("AVD 2 started");
                     }
 
                     @Override
-                    public void onAnimationEnd(Drawable drawable) {
+                    public void onAnimationEnd(@NonNull Drawable drawable) {
                         textView4.setText("AVD 2 Ended");
                     }
                 };
diff --git a/vectordrawable/vectordrawable-animated/api/1.2.0-beta01.txt b/vectordrawable/vectordrawable-animated/api/1.2.0-beta01.txt
index 4c7b6cc..683796b 100644
--- a/vectordrawable/vectordrawable-animated/api/1.2.0-beta01.txt
+++ b/vectordrawable/vectordrawable-animated/api/1.2.0-beta01.txt
@@ -9,27 +9,27 @@
 
   public abstract static class Animatable2Compat.AnimationCallback {
     ctor public Animatable2Compat.AnimationCallback();
-    method public void onAnimationEnd(android.graphics.drawable.Drawable!);
-    method public void onAnimationStart(android.graphics.drawable.Drawable!);
+    method public void onAnimationEnd(android.graphics.drawable.Drawable);
+    method public void onAnimationStart(android.graphics.drawable.Drawable);
   }
 
   public class AnimatedVectorDrawableCompat extends android.graphics.drawable.Drawable implements androidx.vectordrawable.graphics.drawable.Animatable2Compat {
     method public void clearAnimationCallbacks();
-    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable!);
+    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable?);
     method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat? create(android.content.Context, @DrawableRes int);
-    method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat! createFromXmlInner(android.content.Context!, android.content.res.Resources!, org.xmlpull.v1.XmlPullParser!, android.util.AttributeSet!, android.content.res.Resources.Theme!) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas!);
+    method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method public boolean isRunning();
-    method public void registerAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static void registerAnimationCallback(android.graphics.drawable.Drawable!, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback!);
+    method public void registerAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
+    method public static void registerAnimationCallback(android.graphics.drawable.Drawable?, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
     method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setColorFilter(android.graphics.ColorFilter?);
     method public void setColorFilter(int, android.graphics.PorterDuff.Mode!);
     method public void start();
     method public void stop();
-    method public boolean unregisterAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable!, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback!);
+    method public boolean unregisterAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
+    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable?, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
   }
 
 }
diff --git a/vectordrawable/vectordrawable-animated/api/current.txt b/vectordrawable/vectordrawable-animated/api/current.txt
index 4c7b6cc..683796b 100644
--- a/vectordrawable/vectordrawable-animated/api/current.txt
+++ b/vectordrawable/vectordrawable-animated/api/current.txt
@@ -9,27 +9,27 @@
 
   public abstract static class Animatable2Compat.AnimationCallback {
     ctor public Animatable2Compat.AnimationCallback();
-    method public void onAnimationEnd(android.graphics.drawable.Drawable!);
-    method public void onAnimationStart(android.graphics.drawable.Drawable!);
+    method public void onAnimationEnd(android.graphics.drawable.Drawable);
+    method public void onAnimationStart(android.graphics.drawable.Drawable);
   }
 
   public class AnimatedVectorDrawableCompat extends android.graphics.drawable.Drawable implements androidx.vectordrawable.graphics.drawable.Animatable2Compat {
     method public void clearAnimationCallbacks();
-    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable!);
+    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable?);
     method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat? create(android.content.Context, @DrawableRes int);
-    method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat! createFromXmlInner(android.content.Context!, android.content.res.Resources!, org.xmlpull.v1.XmlPullParser!, android.util.AttributeSet!, android.content.res.Resources.Theme!) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas!);
+    method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method public boolean isRunning();
-    method public void registerAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static void registerAnimationCallback(android.graphics.drawable.Drawable!, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback!);
+    method public void registerAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
+    method public static void registerAnimationCallback(android.graphics.drawable.Drawable?, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
     method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setColorFilter(android.graphics.ColorFilter?);
     method public void setColorFilter(int, android.graphics.PorterDuff.Mode!);
     method public void start();
     method public void stop();
-    method public boolean unregisterAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable!, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback!);
+    method public boolean unregisterAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
+    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable?, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
   }
 
 }
diff --git a/vectordrawable/vectordrawable-animated/api/public_plus_experimental_1.2.0-beta01.txt b/vectordrawable/vectordrawable-animated/api/public_plus_experimental_1.2.0-beta01.txt
index 4c7b6cc..683796b 100644
--- a/vectordrawable/vectordrawable-animated/api/public_plus_experimental_1.2.0-beta01.txt
+++ b/vectordrawable/vectordrawable-animated/api/public_plus_experimental_1.2.0-beta01.txt
@@ -9,27 +9,27 @@
 
   public abstract static class Animatable2Compat.AnimationCallback {
     ctor public Animatable2Compat.AnimationCallback();
-    method public void onAnimationEnd(android.graphics.drawable.Drawable!);
-    method public void onAnimationStart(android.graphics.drawable.Drawable!);
+    method public void onAnimationEnd(android.graphics.drawable.Drawable);
+    method public void onAnimationStart(android.graphics.drawable.Drawable);
   }
 
   public class AnimatedVectorDrawableCompat extends android.graphics.drawable.Drawable implements androidx.vectordrawable.graphics.drawable.Animatable2Compat {
     method public void clearAnimationCallbacks();
-    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable!);
+    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable?);
     method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat? create(android.content.Context, @DrawableRes int);
-    method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat! createFromXmlInner(android.content.Context!, android.content.res.Resources!, org.xmlpull.v1.XmlPullParser!, android.util.AttributeSet!, android.content.res.Resources.Theme!) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas!);
+    method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method public boolean isRunning();
-    method public void registerAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static void registerAnimationCallback(android.graphics.drawable.Drawable!, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback!);
+    method public void registerAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
+    method public static void registerAnimationCallback(android.graphics.drawable.Drawable?, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
     method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setColorFilter(android.graphics.ColorFilter?);
     method public void setColorFilter(int, android.graphics.PorterDuff.Mode!);
     method public void start();
     method public void stop();
-    method public boolean unregisterAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable!, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback!);
+    method public boolean unregisterAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
+    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable?, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
   }
 
 }
diff --git a/vectordrawable/vectordrawable-animated/api/public_plus_experimental_current.txt b/vectordrawable/vectordrawable-animated/api/public_plus_experimental_current.txt
index 4c7b6cc..683796b 100644
--- a/vectordrawable/vectordrawable-animated/api/public_plus_experimental_current.txt
+++ b/vectordrawable/vectordrawable-animated/api/public_plus_experimental_current.txt
@@ -9,27 +9,27 @@
 
   public abstract static class Animatable2Compat.AnimationCallback {
     ctor public Animatable2Compat.AnimationCallback();
-    method public void onAnimationEnd(android.graphics.drawable.Drawable!);
-    method public void onAnimationStart(android.graphics.drawable.Drawable!);
+    method public void onAnimationEnd(android.graphics.drawable.Drawable);
+    method public void onAnimationStart(android.graphics.drawable.Drawable);
   }
 
   public class AnimatedVectorDrawableCompat extends android.graphics.drawable.Drawable implements androidx.vectordrawable.graphics.drawable.Animatable2Compat {
     method public void clearAnimationCallbacks();
-    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable!);
+    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable?);
     method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat? create(android.content.Context, @DrawableRes int);
-    method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat! createFromXmlInner(android.content.Context!, android.content.res.Resources!, org.xmlpull.v1.XmlPullParser!, android.util.AttributeSet!, android.content.res.Resources.Theme!) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas!);
+    method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method public boolean isRunning();
-    method public void registerAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static void registerAnimationCallback(android.graphics.drawable.Drawable!, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback!);
+    method public void registerAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
+    method public static void registerAnimationCallback(android.graphics.drawable.Drawable?, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
     method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setColorFilter(android.graphics.ColorFilter?);
     method public void setColorFilter(int, android.graphics.PorterDuff.Mode!);
     method public void start();
     method public void stop();
-    method public boolean unregisterAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable!, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback!);
+    method public boolean unregisterAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
+    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable?, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
   }
 
 }
diff --git a/vectordrawable/vectordrawable-animated/api/restricted_1.2.0-beta01.txt b/vectordrawable/vectordrawable-animated/api/restricted_1.2.0-beta01.txt
index c094267..5221d91 100644
--- a/vectordrawable/vectordrawable-animated/api/restricted_1.2.0-beta01.txt
+++ b/vectordrawable/vectordrawable-animated/api/restricted_1.2.0-beta01.txt
@@ -9,31 +9,31 @@
 
   public abstract static class Animatable2Compat.AnimationCallback {
     ctor public Animatable2Compat.AnimationCallback();
-    method public void onAnimationEnd(android.graphics.drawable.Drawable!);
-    method public void onAnimationStart(android.graphics.drawable.Drawable!);
+    method public void onAnimationEnd(android.graphics.drawable.Drawable);
+    method public void onAnimationStart(android.graphics.drawable.Drawable);
   }
 
   public class AnimatedVectorDrawableCompat extends android.graphics.drawable.Drawable implements androidx.vectordrawable.graphics.drawable.Animatable2Compat androidx.core.graphics.drawable.TintAwareDrawable {
     method public void clearAnimationCallbacks();
-    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable!);
+    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable?);
     method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat? create(android.content.Context, @DrawableRes int);
-    method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat! createFromXmlInner(android.content.Context!, android.content.res.Resources!, org.xmlpull.v1.XmlPullParser!, android.util.AttributeSet!, android.content.res.Resources.Theme!) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas!);
+    method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method public boolean isRunning();
-    method public void registerAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static void registerAnimationCallback(android.graphics.drawable.Drawable!, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback!);
+    method public void registerAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
+    method public static void registerAnimationCallback(android.graphics.drawable.Drawable?, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
     method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setColorFilter(android.graphics.ColorFilter?);
     method public void setColorFilter(int, android.graphics.PorterDuff.Mode!);
     method public void start();
     method public void stop();
-    method public boolean unregisterAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable!, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback!);
+    method public boolean unregisterAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
+    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable?, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class AnimationUtilsCompat {
-    method public static android.view.animation.Interpolator! loadInterpolator(android.content.Context!, int) throws android.content.res.Resources.NotFoundException;
+    method public static android.view.animation.Interpolator loadInterpolator(android.content.Context, @AnimRes int) throws android.content.res.Resources.NotFoundException;
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class AnimatorInflaterCompat {
@@ -42,10 +42,10 @@
     method public static android.animation.Animator! loadAnimator(android.content.Context!, android.content.res.Resources!, android.content.res.Resources.Theme!, @AnimatorRes int, float) throws android.content.res.Resources.NotFoundException;
   }
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ArgbEvaluator implements android.animation.TypeEvaluator {
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ArgbEvaluator implements android.animation.TypeEvaluator<java.lang.Object> {
     ctor public ArgbEvaluator();
     method public Object! evaluate(float, Object!, Object!);
-    method public static androidx.vectordrawable.graphics.drawable.ArgbEvaluator! getInstance();
+    method public static androidx.vectordrawable.graphics.drawable.ArgbEvaluator getInstance();
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class PathInterpolatorCompat implements android.view.animation.Interpolator {
diff --git a/vectordrawable/vectordrawable-animated/api/restricted_current.txt b/vectordrawable/vectordrawable-animated/api/restricted_current.txt
index c094267..5221d91 100644
--- a/vectordrawable/vectordrawable-animated/api/restricted_current.txt
+++ b/vectordrawable/vectordrawable-animated/api/restricted_current.txt
@@ -9,31 +9,31 @@
 
   public abstract static class Animatable2Compat.AnimationCallback {
     ctor public Animatable2Compat.AnimationCallback();
-    method public void onAnimationEnd(android.graphics.drawable.Drawable!);
-    method public void onAnimationStart(android.graphics.drawable.Drawable!);
+    method public void onAnimationEnd(android.graphics.drawable.Drawable);
+    method public void onAnimationStart(android.graphics.drawable.Drawable);
   }
 
   public class AnimatedVectorDrawableCompat extends android.graphics.drawable.Drawable implements androidx.vectordrawable.graphics.drawable.Animatable2Compat androidx.core.graphics.drawable.TintAwareDrawable {
     method public void clearAnimationCallbacks();
-    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable!);
+    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable?);
     method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat? create(android.content.Context, @DrawableRes int);
-    method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat! createFromXmlInner(android.content.Context!, android.content.res.Resources!, org.xmlpull.v1.XmlPullParser!, android.util.AttributeSet!, android.content.res.Resources.Theme!) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas!);
+    method public static androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method public boolean isRunning();
-    method public void registerAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static void registerAnimationCallback(android.graphics.drawable.Drawable!, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback!);
+    method public void registerAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
+    method public static void registerAnimationCallback(android.graphics.drawable.Drawable?, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
     method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setColorFilter(android.graphics.ColorFilter?);
     method public void setColorFilter(int, android.graphics.PorterDuff.Mode!);
     method public void start();
     method public void stop();
-    method public boolean unregisterAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback);
-    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable!, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback!);
+    method public boolean unregisterAnimationCallback(androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
+    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable?, androidx.vectordrawable.graphics.drawable.Animatable2Compat.AnimationCallback?);
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class AnimationUtilsCompat {
-    method public static android.view.animation.Interpolator! loadInterpolator(android.content.Context!, int) throws android.content.res.Resources.NotFoundException;
+    method public static android.view.animation.Interpolator loadInterpolator(android.content.Context, @AnimRes int) throws android.content.res.Resources.NotFoundException;
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class AnimatorInflaterCompat {
@@ -42,10 +42,10 @@
     method public static android.animation.Animator! loadAnimator(android.content.Context!, android.content.res.Resources!, android.content.res.Resources.Theme!, @AnimatorRes int, float) throws android.content.res.Resources.NotFoundException;
   }
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ArgbEvaluator implements android.animation.TypeEvaluator {
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ArgbEvaluator implements android.animation.TypeEvaluator<java.lang.Object> {
     ctor public ArgbEvaluator();
     method public Object! evaluate(float, Object!, Object!);
-    method public static androidx.vectordrawable.graphics.drawable.ArgbEvaluator! getInstance();
+    method public static androidx.vectordrawable.graphics.drawable.ArgbEvaluator getInstance();
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class PathInterpolatorCompat implements android.view.animation.Interpolator {
diff --git a/vectordrawable/vectordrawable-animated/build.gradle b/vectordrawable/vectordrawable-animated/build.gradle
index 8b3b6b3..75c6c2a 100644
--- a/vectordrawable/vectordrawable-animated/build.gradle
+++ b/vectordrawable/vectordrawable-animated/build.gradle
@@ -6,7 +6,9 @@
 }
 
 dependencies {
+    api("androidx.annotation:annotation:1.2.0")
     api(project(":vectordrawable:vectordrawable"))
+    implementation("androidx.core:core:1.6.0")
     implementation("androidx.interpolator:interpolator:1.0.0")
     implementation("androidx.collection:collection:1.1.0")
 
diff --git a/vectordrawable/vectordrawable-animated/src/androidTest/java/androidx/vectordrawable/graphics/drawable/tests/AnimatedVectorDrawableParameterizedTest.java b/vectordrawable/vectordrawable-animated/src/androidTest/java/androidx/vectordrawable/graphics/drawable/tests/AnimatedVectorDrawableParameterizedTest.java
index dcf1e26..5015314 100644
--- a/vectordrawable/vectordrawable-animated/src/androidTest/java/androidx/vectordrawable/graphics/drawable/tests/AnimatedVectorDrawableParameterizedTest.java
+++ b/vectordrawable/vectordrawable-animated/src/androidTest/java/androidx/vectordrawable/graphics/drawable/tests/AnimatedVectorDrawableParameterizedTest.java
@@ -26,6 +26,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 
+import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
@@ -101,12 +102,12 @@
 
         avd.registerAnimationCallback(new AnimationCallback() {
             @Override
-            public void onAnimationStart(Drawable drawable) {
+            public void onAnimationStart(@NonNull Drawable drawable) {
                 // Nothing to do.
             }
 
             @Override
-            public void onAnimationEnd(Drawable drawable) {
+            public void onAnimationEnd(@NonNull Drawable drawable) {
                 bitmap.eraseColor(0);
                 drawable.draw(c);
                 int centerColor = bitmap.getPixel(IMAGE_WIDTH / 2 , IMAGE_WIDTH / 2);
diff --git a/vectordrawable/vectordrawable-animated/src/androidTest/java/androidx/vectordrawable/graphics/drawable/tests/AnimatedVectorDrawableTest.java b/vectordrawable/vectordrawable-animated/src/androidTest/java/androidx/vectordrawable/graphics/drawable/tests/AnimatedVectorDrawableTest.java
index b2fa23a..7aae59ba7 100644
--- a/vectordrawable/vectordrawable-animated/src/androidTest/java/androidx/vectordrawable/graphics/drawable/tests/AnimatedVectorDrawableTest.java
+++ b/vectordrawable/vectordrawable-animated/src/androidTest/java/androidx/vectordrawable/graphics/drawable/tests/AnimatedVectorDrawableTest.java
@@ -39,6 +39,7 @@
 import android.widget.ImageButton;
 
 import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
 import androidx.core.view.ViewCompat;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -93,13 +94,13 @@
             new AnimationCallback() {
                 @Override
                 public void onAnimationStart(
-                        Drawable drawable) {
+                        @NonNull Drawable drawable) {
                     mAnimationStarted = true;
                 }
 
                 @Override
                 public void onAnimationEnd(
-                        Drawable drawable) {
+                        @NonNull Drawable drawable) {
                     mAnimationEnded = true;
                 }
             };
@@ -486,12 +487,12 @@
 
         avd.registerAnimationCallback(new AnimationCallback() {
             @Override
-            public void onAnimationStart(Drawable drawable) {
+            public void onAnimationStart(@NonNull Drawable drawable) {
                 // Nothing to do.
             }
 
             @Override
-            public void onAnimationEnd(Drawable drawable) {
+            public void onAnimationEnd(@NonNull Drawable drawable) {
                 bitmap.eraseColor(0);
                 drawable.draw(c);
                 int centerColor = bitmap.getPixel(IMAGE_WIDTH / 2 , IMAGE_WIDTH / 2);
diff --git a/vectordrawable/vectordrawable-animated/src/androidTest/java/androidx/vectordrawable/graphics/drawable/tests/PathInterpolatorParameterizedTest.java b/vectordrawable/vectordrawable-animated/src/androidTest/java/androidx/vectordrawable/graphics/drawable/tests/PathInterpolatorParameterizedTest.java
index d643923..c33a254 100644
--- a/vectordrawable/vectordrawable-animated/src/androidTest/java/androidx/vectordrawable/graphics/drawable/tests/PathInterpolatorParameterizedTest.java
+++ b/vectordrawable/vectordrawable-animated/src/androidTest/java/androidx/vectordrawable/graphics/drawable/tests/PathInterpolatorParameterizedTest.java
@@ -23,6 +23,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 
+import androidx.annotation.NonNull;
 import androidx.test.filters.MediumTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
@@ -91,12 +92,12 @@
 
         avd.registerAnimationCallback(new Animatable2Compat.AnimationCallback() {
             @Override
-            public void onAnimationStart(Drawable drawable) {
+            public void onAnimationStart(@NonNull Drawable drawable) {
                 // Nothing to do.
             }
 
             @Override
-            public void onAnimationEnd(Drawable drawable) {
+            public void onAnimationEnd(@NonNull Drawable drawable) {
                 bitmap.eraseColor(0);
                 drawable.draw(c);
                 int centerColor = bitmap.getPixel(IMAGE_WIDTH / 2 , IMAGE_WIDTH / 2);
diff --git a/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/Animatable2Compat.java b/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/Animatable2Compat.java
index 7c61989..f5f900c 100644
--- a/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/Animatable2Compat.java
+++ b/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/Animatable2Compat.java
@@ -60,13 +60,13 @@
          *
          * @param drawable The drawable started the animation.
          */
-        public void onAnimationStart(Drawable drawable) {};
+        public void onAnimationStart(@NonNull Drawable drawable) {}
         /**
          * Called when the animation ends.
          *
          * @param drawable The drawable finished the animation.
          */
-        public void onAnimationEnd(Drawable drawable) {};
+        public void onAnimationEnd(@NonNull Drawable drawable) {}
 
         // Only when passing this Animatable2Compat.AnimationCallback to a frameworks' AVD, we need
         // to bridge this compat version callback with the frameworks' callback.
diff --git a/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatedVectorDrawableCompat.java b/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatedVectorDrawableCompat.java
index 78c2111..7c743a9 100644
--- a/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatedVectorDrawableCompat.java
+++ b/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatedVectorDrawableCompat.java
@@ -21,6 +21,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -31,6 +32,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Animatable2;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
@@ -38,6 +40,7 @@
 import android.util.Log;
 import android.util.Xml;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -46,6 +49,7 @@
 import androidx.core.content.res.ResourcesCompat;
 import androidx.core.content.res.TypedArrayUtils;
 import androidx.core.graphics.drawable.DrawableCompat;
+import androidx.core.util.ObjectsCompat;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -57,8 +61,8 @@
 /**
  * For API 24 and above, this class is delegating to the framework's {@link
  * AnimatedVectorDrawable}.
- * For older API version, this class uses {@link android.animation.ObjectAnimator} and
- * {@link android.animation.AnimatorSet} to animate the properties of a
+ * For older API version, this class uses {@link ObjectAnimator} and
+ * {@link AnimatorSet} to animate the properties of a
  * {@link VectorDrawableCompat} to create an animated drawable.
  * <p/>
  * AnimatedVectorDrawableCompat are defined in the same XML format as
@@ -151,9 +155,10 @@
 
     private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
 
-    private AnimatedVectorDrawableCompatState mAnimatedVectorState;
+    @NonNull
+    private final AnimatedVectorDrawableCompatState mAnimatedVectorState;
 
-    private Context mContext;
+    private final Context mContext;
 
     private ArgbEvaluator mArgbEvaluator = null;
 
@@ -163,7 +168,7 @@
     private Animator.AnimatorListener mAnimatorListener = null;
 
     // Use an array to keep track of multiple call back associated with one drawable.
-    ArrayList<Animatable2Compat.AnimationCallback> mAnimationCallbacks = null;
+    ArrayList<AnimationCallback> mAnimationCallbacks = null;
 
 
     AnimatedVectorDrawableCompat() {
@@ -181,7 +186,7 @@
         if (state != null) {
             mAnimatedVectorState = state;
         } else {
-            mAnimatedVectorState = new AnimatedVectorDrawableCompatState(context, state, mCallback,
+            mAnimatedVectorState = new AnimatedVectorDrawableCompatState(context, null, mCallback,
                     res);
         }
     }
@@ -190,6 +195,7 @@
      * mutate() will be effective only if the getConstantState() is returning non-null.
      * Otherwise, it just return the current object without modification.
      */
+    @NonNull
     @Override
     public Drawable mutate() {
         if (mDelegateDrawable != null) {
@@ -213,11 +219,14 @@
             @DrawableRes int resId) {
         if (Build.VERSION.SDK_INT >= 24) {
             final AnimatedVectorDrawableCompat drawable = new AnimatedVectorDrawableCompat(context);
-            drawable.mDelegateDrawable = ResourcesCompat.getDrawable(context.getResources(), resId,
+            final Drawable delegate = ResourcesCompat.getDrawable(context.getResources(), resId,
                     context.getTheme());
-            drawable.mDelegateDrawable.setCallback(drawable.mCallback);
+            ObjectsCompat.requireNonNull(drawable, "Failed to load drawable");
+            //noinspection ConstantConditions
+            delegate.setCallback(drawable.mCallback);
             drawable.mCachedConstantStateDelegate = new AnimatedVectorDrawableDelegateState(
-                    drawable.mDelegateDrawable.getConstantState());
+                    delegate.getConstantState());
+            drawable.mDelegateDrawable = delegate;
             return drawable;
         }
         Resources resources = context.getResources();
@@ -226,6 +235,7 @@
             final XmlPullParser parser = resources.getXml(resId);
             final AttributeSet attrs = Xml.asAttributeSet(parser);
             int type;
+            //noinspection StatementWithEmptyBody
             while ((type = parser.next()) != XmlPullParser.START_TAG
                     && type != XmlPullParser.END_DOCUMENT) {
                 // Empty loop
@@ -249,9 +259,10 @@
      * document, tries to create a Drawable from that tag. Returns {@code null}
      * if the tag is not a valid drawable.
      */
-    public static AnimatedVectorDrawableCompat createFromXmlInner(Context context, Resources r,
-            XmlPullParser parser, AttributeSet attrs, Theme theme)
-            throws XmlPullParserException, IOException {
+    @NonNull
+    public static AnimatedVectorDrawableCompat createFromXmlInner(@NonNull Context context,
+            @NonNull Resources r, @NonNull XmlPullParser parser, @NonNull AttributeSet attrs,
+            @Nullable Theme theme) throws XmlPullParserException, IOException {
         final AnimatedVectorDrawableCompat drawable = new AnimatedVectorDrawableCompat(context);
         drawable.inflate(r, parser, attrs, theme);
         return drawable;
@@ -262,6 +273,7 @@
      * <strong>Note</strong> that we don't support constant state when SDK < 24.
      * Make sure you check the return value before using it.
      */
+    @Nullable
     @Override
     public ConstantState getConstantState() {
         if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 24) {
@@ -282,7 +294,7 @@
     }
 
     @Override
-    public void draw(Canvas canvas) {
+    public void draw(@NonNull Canvas canvas) {
         if (mDelegateDrawable != null) {
             mDelegateDrawable.draw(canvas);
             return;
@@ -336,7 +348,7 @@
     }
 
     @Override
-    public void setColorFilter(ColorFilter colorFilter) {
+    public void setColorFilter(@Nullable ColorFilter colorFilter) {
         if (mDelegateDrawable != null) {
             mDelegateDrawable.setColorFilter(colorFilter);
             return;
@@ -344,6 +356,7 @@
         mAnimatedVectorState.mVectorDrawable.setColorFilter(colorFilter);
     }
 
+    @Nullable
     @Override
     public ColorFilter getColorFilter() {
         if (mDelegateDrawable != null) {
@@ -363,7 +376,7 @@
     }
 
     @Override
-    public void setTintList(ColorStateList tint) {
+    public void setTintList(@Nullable ColorStateList tint) {
         if (mDelegateDrawable != null) {
             DrawableCompat.setTintList(mDelegateDrawable, tint);
             return;
@@ -373,7 +386,7 @@
     }
 
     @Override
-    public void setTintMode(PorterDuff.Mode tintMode) {
+    public void setTintMode(@Nullable PorterDuff.Mode tintMode) {
         if (mDelegateDrawable != null) {
             DrawableCompat.setTintMode(mDelegateDrawable, tintMode);
             return;
@@ -399,8 +412,6 @@
         return mAnimatedVectorState.mVectorDrawable.isStateful();
     }
 
-    // Remove deprecation suppression once b/120984759 is resolved
-    @SuppressWarnings("deprecation")
     @Override
     public int getOpacity() {
         if (mDelegateDrawable != null) {
@@ -443,7 +454,8 @@
     }
 
     @Override
-    public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
+    public void inflate(@NonNull Resources res, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs, @Nullable Theme theme)
             throws XmlPullParserException, IOException {
         if (mDelegateDrawable != null) {
             DrawableCompat.inflate(mDelegateDrawable, res, parser, attrs, theme);
@@ -473,6 +485,7 @@
                     if (drawableRes != 0) {
                         VectorDrawableCompat vectorDrawable = VectorDrawableCompat.create(res,
                                 drawableRes, theme);
+                        ObjectsCompat.requireNonNull(vectorDrawable, "Failed to load drawable");
                         vectorDrawable.setAllowCaching(false);
                         vectorDrawable.setCallback(mCallback);
                         if (mAnimatedVectorState.mVectorDrawable != null) {
@@ -482,8 +495,7 @@
                     }
                     a.recycle();
                 } else if (TARGET.equals(tagName)) {
-                    final TypedArray a =
-                            res.obtainAttributes(attrs,
+                    final TypedArray a = res.obtainAttributes(attrs,
                                     AndroidResources.STYLEABLE_ANIMATED_VECTOR_DRAWABLE_TARGET);
                     final String target = a.getString(
                             AndroidResources.STYLEABLE_ANIMATED_VECTOR_DRAWABLE_TARGET_NAME);
@@ -514,19 +526,17 @@
     }
 
     @Override
-    public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs)
-            throws XmlPullParserException, IOException {
+    public void inflate(@NonNull Resources res, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs) throws XmlPullParserException, IOException {
         inflate(res, parser, attrs, null);
     }
 
     @Override
-    public void applyTheme(Theme t) {
+    public void applyTheme(@NonNull Theme t) {
         if (mDelegateDrawable != null) {
             DrawableCompat.applyTheme(mDelegateDrawable, t);
-            return;
         }
         // TODO: support theming in older platform.
-        return;
     }
 
     @Override
@@ -597,6 +607,7 @@
         ArrayList<Animator> mAnimators;
         ArrayMap<Animator, String> mTargetNameMap;
 
+        @SuppressWarnings("unused")
         AnimatedVectorDrawableCompatState(Context context,
                 AnimatedVectorDrawableCompatState copy, Callback owner, Resources res) {
             if (copy != null) {
@@ -615,8 +626,8 @@
                 }
                 if (copy.mAnimators != null) {
                     final int numAnimators = copy.mAnimators.size();
-                    mAnimators = new ArrayList<Animator>(numAnimators);
-                    mTargetNameMap = new ArrayMap<Animator, String>(numAnimators);
+                    mAnimators = new ArrayList<>(numAnimators);
+                    mTargetNameMap = new ArrayMap<>(numAnimators);
                     for (int i = 0; i < numAnimators; ++i) {
                         Animator anim = copy.mAnimators.get(i);
                         Animator animClone = anim.clone();
@@ -687,8 +698,8 @@
             setupColorAnimator(animator);
         }
         if (mAnimatedVectorState.mAnimators == null) {
-            mAnimatedVectorState.mAnimators = new ArrayList<Animator>();
-            mAnimatedVectorState.mTargetNameMap = new ArrayMap<Animator, String>();
+            mAnimatedVectorState.mAnimators = new ArrayList<>();
+            mAnimatedVectorState.mTargetNameMap = new ArrayMap<>();
         }
         mAnimatedVectorState.mAnimators.add(animator);
         mAnimatedVectorState.mTargetNameMap.put(animator, name);
@@ -697,19 +708,19 @@
         }
     }
 
+    @SuppressLint("NewApi") // mDelegateDrawable != null is an implicit API check
     @Override
     public boolean isRunning() {
         if (mDelegateDrawable != null) {
-            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
             return ((AnimatedVectorDrawable) mDelegateDrawable).isRunning();
         }
         return mAnimatedVectorState.mAnimatorSet.isRunning();
     }
 
+    @SuppressLint("NewApi") // mDelegateDrawable != null is an implicit API check
     @Override
     public void start() {
         if (mDelegateDrawable != null) {
-            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
             ((AnimatedVectorDrawable) mDelegateDrawable).start();
             return;
         }
@@ -722,10 +733,10 @@
         invalidateSelf();
     }
 
+    @SuppressLint("NewApi") // mDelegateDrawable != null is an implicit API check
     @Override
     public void stop() {
         if (mDelegateDrawable != null) {
-            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
             ((AnimatedVectorDrawable) mDelegateDrawable).stop();
             return;
         }
@@ -755,20 +766,19 @@
      */
     @RequiresApi(23)
     private static boolean unregisterPlatformCallback(AnimatedVectorDrawable dr,
-            Animatable2Compat.AnimationCallback callback) {
-        return dr.unregisterAnimationCallback(callback.getPlatformCallback());
+            AnimationCallback callback) {
+        return Api23Impl.unregisterAnimationCallback(dr, callback.getPlatformCallback());
     }
 
     @Override
-    public void registerAnimationCallback(@NonNull Animatable2Compat.AnimationCallback
-            callback) {
-        if (mDelegateDrawable != null) {
-            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
-            registerPlatformCallback((AnimatedVectorDrawable) mDelegateDrawable, callback);
+    public void registerAnimationCallback(@Nullable AnimationCallback callback) {
+        if (callback == null) {
             return;
         }
 
-        if (callback == null) {
+        if (mDelegateDrawable != null) {
+            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
+            registerPlatformCallback((AnimatedVectorDrawable) mDelegateDrawable, callback);
             return;
         }
 
@@ -790,7 +800,7 @@
             mAnimatorListener = new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationStart(Animator animation) {
-                    ArrayList<Animatable2Compat.AnimationCallback> tmpCallbacks =
+                    ArrayList<AnimationCallback> tmpCallbacks =
                             new ArrayList<>(mAnimationCallbacks);
                     int size = tmpCallbacks.size();
                     for (int i = 0; i < size; i++) {
@@ -800,7 +810,7 @@
 
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    ArrayList<Animatable2Compat.AnimationCallback> tmpCallbacks =
+                    ArrayList<AnimationCallback> tmpCallbacks =
                             new ArrayList<>(mAnimationCallbacks);
                     int size = tmpCallbacks.size();
                     for (int i = 0; i < size; i++) {
@@ -818,8 +828,8 @@
      */
     @RequiresApi(23)
     private static void registerPlatformCallback(@NonNull AnimatedVectorDrawable avd,
-            @NonNull final Animatable2Compat.AnimationCallback callback) {
-        avd.registerAnimationCallback(callback.getPlatformCallback());
+            @NonNull final AnimationCallback callback) {
+        Api23Impl.registerAnimationCallback(avd, callback.getPlatformCallback());
     }
 
     /**
@@ -833,14 +843,18 @@
     }
 
     @Override
-    public boolean unregisterAnimationCallback(
-            @NonNull Animatable2Compat.AnimationCallback callback) {
+    public boolean unregisterAnimationCallback(@Nullable AnimationCallback callback) {
+        if (callback == null) {
+            // Nothing to be removed.
+            return false;
+        }
+
         if (mDelegateDrawable != null) {
             //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
             unregisterPlatformCallback((AnimatedVectorDrawable) mDelegateDrawable, callback);
         }
 
-        if (mAnimationCallbacks == null || callback == null) {
+        if (mAnimationCallbacks == null) {
             // Nothing to be removed.
             return false;
         }
@@ -853,11 +867,11 @@
         return removed;
     }
 
+    @SuppressLint("NewApi")
     @Override
     public void clearAnimationCallbacks() {
         if (mDelegateDrawable != null) {
-            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
-            ((AnimatedVectorDrawable) mDelegateDrawable).clearAnimationCallbacks();
+            Api23Impl.clearAnimationCallbacks(mDelegateDrawable);
             return;
         }
         removeAnimatorSetListener();
@@ -874,8 +888,8 @@
      * From API 24 on, the drawable is treated as an AnimatedVectorDrawable.
      * Otherwise, it is treated as AnimatedVectorDrawableCompat.
      */
-    public static void registerAnimationCallback(Drawable dr,
-            Animatable2Compat.AnimationCallback callback) {
+    public static void registerAnimationCallback(@Nullable Drawable dr,
+            @Nullable AnimationCallback callback) {
         if (dr == null || callback == null) {
             return;
         }
@@ -896,8 +910,8 @@
      * From API 24 on, the drawable is treated as an AnimatedVectorDrawable.
      * Otherwise, it is treated as AnimatedVectorDrawableCompat.
      */
-    public static boolean unregisterAnimationCallback(Drawable dr,
-            Animatable2Compat.AnimationCallback callback) {
+    public static boolean unregisterAnimationCallback(@Nullable Drawable dr,
+            @Nullable AnimationCallback callback) {
         if (dr == null || callback == null) {
             return false;
         }
@@ -918,15 +932,40 @@
      * From API 24 on, the drawable is treated as an AnimatedVectorDrawable.
      * Otherwise, it is treated as AnimatedVectorDrawableCompat.
      */
-    public static void clearAnimationCallbacks(Drawable dr) {
+    public static void clearAnimationCallbacks(@Nullable Drawable dr) {
         if (!(dr instanceof Animatable)) {
             return;
         }
         if (Build.VERSION.SDK_INT >= 24) {
-            ((AnimatedVectorDrawable) dr).clearAnimationCallbacks();
+            Api23Impl.clearAnimationCallbacks(dr);
         } else {
             ((AnimatedVectorDrawableCompat) dr).clearAnimationCallbacks();
         }
 
     }
+
+    @RequiresApi(23)
+    static class Api23Impl {
+        private Api23Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static boolean unregisterAnimationCallback(Object animatedVectorDrawable,
+                Object callback) {
+            return ((AnimatedVectorDrawable) animatedVectorDrawable).unregisterAnimationCallback(
+                    (Animatable2.AnimationCallback) callback);
+        }
+
+        @DoNotInline
+        static void clearAnimationCallbacks(Object animatedVectorDrawable) {
+            ((AnimatedVectorDrawable) animatedVectorDrawable).clearAnimationCallbacks();
+        }
+
+        @DoNotInline
+        static void registerAnimationCallback(Object animatedVectorDrawable, Object callback) {
+            ((AnimatedVectorDrawable) animatedVectorDrawable).registerAnimationCallback(
+                    (Animatable2.AnimationCallback) callback);
+        }
+    }
 }
diff --git a/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat.java b/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat.java
index ec578fc..dbe3039 100644
--- a/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat.java
+++ b/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat.java
@@ -36,7 +36,10 @@
 import android.view.animation.LinearInterpolator;
 import android.view.animation.OvershootInterpolator;
 
+import androidx.annotation.AnimRes;
+import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
+import androidx.core.util.ObjectsCompat;
 import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
 import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
 import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
@@ -59,11 +62,16 @@
      * @param id      The resource id of the animation to load
      * @return The animation object reference by the specified id
      */
-    public static Interpolator loadInterpolator(Context context, int id)
+    @SuppressWarnings("UnnecessaryInitCause") // requires API 24+
+    @NonNull
+    public static Interpolator loadInterpolator(@NonNull Context context, @AnimRes int id)
             throws NotFoundException {
         // From API 21, we added path Interpolator .
         if (Build.VERSION.SDK_INT >= 21) {
-            return AnimationUtils.loadInterpolator(context, id);
+            Interpolator interp = AnimationUtils.loadInterpolator(context, id);
+            ObjectsCompat.requireNonNull(interp, "Failed to parse interpolator, no start tag "
+                    + "found");
+            return interp;
         }
 
         XmlResourceParser parser = null;
@@ -94,9 +102,9 @@
 
     }
 
-    private static Interpolator createInterpolatorFromXml(Context context,
-            XmlPullParser parser)
-            throws XmlPullParserException, IOException {
+    @NonNull
+    private static Interpolator createInterpolatorFromXml(@NonNull Context context,
+            @NonNull XmlPullParser parser) throws XmlPullParserException, IOException {
 
         Interpolator interpolator = null;
 
@@ -115,30 +123,46 @@
 
             String name = parser.getName();
 
-            if (name.equals("linearInterpolator")) {
-                interpolator = new LinearInterpolator();
-            } else if (name.equals("accelerateInterpolator")) {
-                interpolator = new AccelerateInterpolator(context, attrs);
-            } else if (name.equals("decelerateInterpolator")) {
-                interpolator = new DecelerateInterpolator(context, attrs);
-            } else if (name.equals("accelerateDecelerateInterpolator")) {
-                interpolator = new AccelerateDecelerateInterpolator();
-            } else if (name.equals("cycleInterpolator")) {
-                interpolator = new CycleInterpolator(context, attrs);
-            } else if (name.equals("anticipateInterpolator")) {
-                interpolator = new AnticipateInterpolator(context, attrs);
-            } else if (name.equals("overshootInterpolator")) {
-                interpolator = new OvershootInterpolator(context, attrs);
-            } else if (name.equals("anticipateOvershootInterpolator")) {
-                interpolator = new AnticipateOvershootInterpolator(context, attrs);
-            } else if (name.equals("bounceInterpolator")) {
-                interpolator = new BounceInterpolator();
-            } else if (name.equals("pathInterpolator")) {
-                interpolator = new PathInterpolatorCompat(context, attrs, parser);
-            } else {
-                throw new RuntimeException("Unknown interpolator name: " + parser.getName());
+            switch (name) {
+                case "linearInterpolator":
+                    interpolator = new LinearInterpolator();
+                    break;
+                case "accelerateInterpolator":
+                    interpolator = new AccelerateInterpolator(context, attrs);
+                    break;
+                case "decelerateInterpolator":
+                    interpolator = new DecelerateInterpolator(context, attrs);
+                    break;
+                case "accelerateDecelerateInterpolator":
+                    interpolator = new AccelerateDecelerateInterpolator();
+                    break;
+                case "cycleInterpolator":
+                    interpolator = new CycleInterpolator(context, attrs);
+                    break;
+                case "anticipateInterpolator":
+                    interpolator = new AnticipateInterpolator(context, attrs);
+                    break;
+                case "overshootInterpolator":
+                    interpolator = new OvershootInterpolator(context, attrs);
+                    break;
+                case "anticipateOvershootInterpolator":
+                    interpolator = new AnticipateOvershootInterpolator(context, attrs);
+                    break;
+                case "bounceInterpolator":
+                    interpolator = new BounceInterpolator();
+                    break;
+                case "pathInterpolator":
+                    interpolator = new PathInterpolatorCompat(context, attrs, parser);
+                    break;
+                default:
+                    throw new RuntimeException("Unknown interpolator name: " + parser.getName());
             }
         }
+
+        if (interpolator == null) {
+            throw new RuntimeException("Failed to parse interpolator, no start tag found");
+        }
+
         return interpolator;
     }
 
diff --git a/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/ArgbEvaluator.java b/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/ArgbEvaluator.java
index 793659e..73e0b63 100644
--- a/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/ArgbEvaluator.java
+++ b/vectordrawable/vectordrawable-animated/src/main/java/androidx/vectordrawable/graphics/drawable/ArgbEvaluator.java
@@ -21,6 +21,7 @@
 import android.animation.TypeEvaluator;
 import android.animation.ValueAnimator;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 
 /**
@@ -29,7 +30,7 @@
  * @hide
  */
 @RestrictTo(LIBRARY_GROUP_PREFIX)
-public class ArgbEvaluator implements TypeEvaluator {
+public class ArgbEvaluator implements TypeEvaluator<Object> {
     private static final ArgbEvaluator sInstance = new ArgbEvaluator();
 
     /**
@@ -39,6 +40,7 @@
      *
      * @return An instance of <code>ArgbEvalutor</code>.
      */
+    @NonNull
     public static ArgbEvaluator getInstance() {
         return sInstance;
     }
diff --git a/vectordrawable/vectordrawable/api/1.2.0-beta02.txt b/vectordrawable/vectordrawable/api/1.2.0-beta02.txt
index 5a6c00b..0285c8e 100644
--- a/vectordrawable/vectordrawable/api/1.2.0-beta02.txt
+++ b/vectordrawable/vectordrawable/api/1.2.0-beta02.txt
@@ -3,11 +3,11 @@
 
   public class VectorDrawableCompat extends android.graphics.drawable.Drawable {
     method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat? create(android.content.res.Resources, @DrawableRes int, android.content.res.Resources.Theme?);
-    method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat! createFromXmlInner(android.content.res.Resources!, org.xmlpull.v1.XmlPullParser!, android.util.AttributeSet!, android.content.res.Resources.Theme!) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas!);
+    method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setColorFilter(android.graphics.ColorFilter?);
     method public void setColorFilter(int, android.graphics.PorterDuff.Mode!);
   }
 
diff --git a/vectordrawable/vectordrawable/api/current.txt b/vectordrawable/vectordrawable/api/current.txt
index 5a6c00b..0285c8e 100644
--- a/vectordrawable/vectordrawable/api/current.txt
+++ b/vectordrawable/vectordrawable/api/current.txt
@@ -3,11 +3,11 @@
 
   public class VectorDrawableCompat extends android.graphics.drawable.Drawable {
     method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat? create(android.content.res.Resources, @DrawableRes int, android.content.res.Resources.Theme?);
-    method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat! createFromXmlInner(android.content.res.Resources!, org.xmlpull.v1.XmlPullParser!, android.util.AttributeSet!, android.content.res.Resources.Theme!) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas!);
+    method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setColorFilter(android.graphics.ColorFilter?);
     method public void setColorFilter(int, android.graphics.PorterDuff.Mode!);
   }
 
diff --git a/vectordrawable/vectordrawable/api/public_plus_experimental_1.2.0-beta02.txt b/vectordrawable/vectordrawable/api/public_plus_experimental_1.2.0-beta02.txt
index 5a6c00b..0285c8e 100644
--- a/vectordrawable/vectordrawable/api/public_plus_experimental_1.2.0-beta02.txt
+++ b/vectordrawable/vectordrawable/api/public_plus_experimental_1.2.0-beta02.txt
@@ -3,11 +3,11 @@
 
   public class VectorDrawableCompat extends android.graphics.drawable.Drawable {
     method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat? create(android.content.res.Resources, @DrawableRes int, android.content.res.Resources.Theme?);
-    method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat! createFromXmlInner(android.content.res.Resources!, org.xmlpull.v1.XmlPullParser!, android.util.AttributeSet!, android.content.res.Resources.Theme!) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas!);
+    method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setColorFilter(android.graphics.ColorFilter?);
     method public void setColorFilter(int, android.graphics.PorterDuff.Mode!);
   }
 
diff --git a/vectordrawable/vectordrawable/api/public_plus_experimental_current.txt b/vectordrawable/vectordrawable/api/public_plus_experimental_current.txt
index 5a6c00b..0285c8e 100644
--- a/vectordrawable/vectordrawable/api/public_plus_experimental_current.txt
+++ b/vectordrawable/vectordrawable/api/public_plus_experimental_current.txt
@@ -3,11 +3,11 @@
 
   public class VectorDrawableCompat extends android.graphics.drawable.Drawable {
     method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat? create(android.content.res.Resources, @DrawableRes int, android.content.res.Resources.Theme?);
-    method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat! createFromXmlInner(android.content.res.Resources!, org.xmlpull.v1.XmlPullParser!, android.util.AttributeSet!, android.content.res.Resources.Theme!) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas!);
+    method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setColorFilter(android.graphics.ColorFilter?);
     method public void setColorFilter(int, android.graphics.PorterDuff.Mode!);
   }
 
diff --git a/vectordrawable/vectordrawable/api/restricted_1.2.0-beta02.txt b/vectordrawable/vectordrawable/api/restricted_1.2.0-beta02.txt
index 30d66c1..89a2875 100644
--- a/vectordrawable/vectordrawable/api/restricted_1.2.0-beta02.txt
+++ b/vectordrawable/vectordrawable/api/restricted_1.2.0-beta02.txt
@@ -3,12 +3,12 @@
 
   public class VectorDrawableCompat extends android.graphics.drawable.Drawable implements androidx.core.graphics.drawable.TintAwareDrawable {
     method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat? create(android.content.res.Resources, @DrawableRes int, android.content.res.Resources.Theme?);
-    method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat! createFromXmlInner(android.content.res.Resources!, org.xmlpull.v1.XmlPullParser!, android.util.AttributeSet!, android.content.res.Resources.Theme!) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas!);
+    method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public float getPixelSize();
     method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setColorFilter(android.graphics.ColorFilter?);
     method public void setColorFilter(int, android.graphics.PorterDuff.Mode!);
   }
 
diff --git a/vectordrawable/vectordrawable/api/restricted_current.txt b/vectordrawable/vectordrawable/api/restricted_current.txt
index 30d66c1..89a2875 100644
--- a/vectordrawable/vectordrawable/api/restricted_current.txt
+++ b/vectordrawable/vectordrawable/api/restricted_current.txt
@@ -3,12 +3,12 @@
 
   public class VectorDrawableCompat extends android.graphics.drawable.Drawable implements androidx.core.graphics.drawable.TintAwareDrawable {
     method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat? create(android.content.res.Resources, @DrawableRes int, android.content.res.Resources.Theme?);
-    method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat! createFromXmlInner(android.content.res.Resources!, org.xmlpull.v1.XmlPullParser!, android.util.AttributeSet!, android.content.res.Resources.Theme!) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public void draw(android.graphics.Canvas!);
+    method public static androidx.vectordrawable.graphics.drawable.VectorDrawableCompat createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public float getPixelSize();
     method public void setAlpha(int);
-    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setColorFilter(android.graphics.ColorFilter?);
     method public void setColorFilter(int, android.graphics.PorterDuff.Mode!);
   }
 
diff --git a/vectordrawable/vectordrawable/build.gradle b/vectordrawable/vectordrawable/build.gradle
index 8e6ae22..b345be1 100644
--- a/vectordrawable/vectordrawable/build.gradle
+++ b/vectordrawable/vectordrawable/build.gradle
@@ -7,7 +7,7 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.1.0")
-    api("androidx.core:core:1.1.0")
+    api("androidx.core:core:1.6.0")
     implementation("androidx.collection:collection:1.1.0")
 
     androidTestImplementation(libs.testExtJunit)
diff --git a/vectordrawable/vectordrawable/src/main/java/androidx/vectordrawable/graphics/drawable/AndroidResources.java b/vectordrawable/vectordrawable/src/main/java/androidx/vectordrawable/graphics/drawable/AndroidResources.java
index a66fa2e..b76c700 100644
--- a/vectordrawable/vectordrawable/src/main/java/androidx/vectordrawable/graphics/drawable/AndroidResources.java
+++ b/vectordrawable/vectordrawable/src/main/java/androidx/vectordrawable/graphics/drawable/AndroidResources.java
@@ -16,8 +16,11 @@
 
 package androidx.vectordrawable.graphics.drawable;
 
+import android.annotation.SuppressLint;
+
 import androidx.annotation.StyleableRes;
 
+@SuppressLint("ResourceType") // We're doing something non-standard but correct.
 class AndroidResources {
 
     // Resources ID generated in the latest R.java for framework.
@@ -69,6 +72,8 @@
     static final int STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_OFFSET = 7;
     static final int STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_START = 5;
     static final int STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_FILLTYPE = 13;
+
+    @StyleableRes
     static final int[] STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH = {
             android.R.attr.name, android.R.attr.pathData, android.R.attr.fillType
     };
@@ -76,10 +81,13 @@
     static final int STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH_PATH_DATA = 1;
     static final int STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH_FILLTYPE = 2;
 
+    @StyleableRes
     static final int[] STYLEABLE_ANIMATED_VECTOR_DRAWABLE = {
             android.R.attr.drawable
     };
     static final int STYLEABLE_ANIMATED_VECTOR_DRAWABLE_DRAWABLE = 0;
+
+    @StyleableRes
     static final int[] STYLEABLE_ANIMATED_VECTOR_DRAWABLE_TARGET = {
             android.R.attr.name, android.R.attr.animation
     };
@@ -121,6 +129,7 @@
     };
     public static final int STYLEABLE_KEYFRAME_VALUE = 0;
     public static final int STYLEABLE_KEYFRAME_INTERPOLATOR = 1;
+    @SuppressWarnings("unused")
     public static final int STYLEABLE_KEYFRAME_VALUE_TYPE = 2;
     public static final int STYLEABLE_KEYFRAME_FRACTION = 3;
 
diff --git a/vectordrawable/vectordrawable/src/main/java/androidx/vectordrawable/graphics/drawable/VectorDrawableCommon.java b/vectordrawable/vectordrawable/src/main/java/androidx/vectordrawable/graphics/drawable/VectorDrawableCommon.java
index ec3cf92..d6b87af 100644
--- a/vectordrawable/vectordrawable/src/main/java/androidx/vectordrawable/graphics/drawable/VectorDrawableCommon.java
+++ b/vectordrawable/vectordrawable/src/main/java/androidx/vectordrawable/graphics/drawable/VectorDrawableCommon.java
@@ -22,6 +22,7 @@
 import android.graphics.Region;
 import android.graphics.drawable.Drawable;
 
+import androidx.annotation.NonNull;
 import androidx.core.graphics.drawable.DrawableCompat;
 import androidx.core.graphics.drawable.TintAwareDrawable;
 
@@ -64,14 +65,12 @@
         if (mDelegateDrawable != null) {
             DrawableCompat.setHotspot(mDelegateDrawable, x, y);
         }
-        return;
     }
 
     @Override
     public void setHotspotBounds(int left, int top, int right, int bottom) {
         if (mDelegateDrawable != null) {
             DrawableCompat.setHotspotBounds(mDelegateDrawable, left, top, right, bottom);
-            return;
         }
     }
 
@@ -79,24 +78,22 @@
     public void setFilterBitmap(boolean filter) {
         if (mDelegateDrawable != null) {
             mDelegateDrawable.setFilterBitmap(filter);
-            return;
         }
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     public void jumpToCurrentState() {
         if (mDelegateDrawable != null) {
             DrawableCompat.jumpToCurrentState(mDelegateDrawable);
-            return;
         }
     }
 
     @Override
-    public void applyTheme(Resources.Theme t) {
+    public void applyTheme(@NonNull Resources.Theme t) {
         // API >= 21 only.
         if (mDelegateDrawable != null) {
             DrawableCompat.applyTheme(mDelegateDrawable, t);
-            return;
         }
     }
 
diff --git a/vectordrawable/vectordrawable/src/main/java/androidx/vectordrawable/graphics/drawable/VectorDrawableCompat.java b/vectordrawable/vectordrawable/src/main/java/androidx/vectordrawable/graphics/drawable/VectorDrawableCompat.java
index ddde6ed..566ad9d 100644
--- a/vectordrawable/vectordrawable/src/main/java/androidx/vectordrawable/graphics/drawable/VectorDrawableCompat.java
+++ b/vectordrawable/vectordrawable/src/main/java/androidx/vectordrawable/graphics/drawable/VectorDrawableCompat.java
@@ -325,6 +325,7 @@
         mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
     }
 
+    @NonNull
     @Override
     public Drawable mutate() {
         if (mDelegateDrawable != null) {
@@ -343,6 +344,7 @@
         return mVectorState.mVPathRenderer.mVGTargetsMap.get(name);
     }
 
+    @NonNull
     @Override
     public ConstantState getConstantState() {
         if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 24) {
@@ -354,7 +356,7 @@
     }
 
     @Override
-    public void draw(Canvas canvas) {
+    public void draw(@NonNull Canvas canvas) {
         if (mDelegateDrawable != null) {
             mDelegateDrawable.draw(canvas);
             return;
@@ -448,7 +450,7 @@
     }
 
     @Override
-    public void setColorFilter(ColorFilter colorFilter) {
+    public void setColorFilter(@Nullable ColorFilter colorFilter) {
         if (mDelegateDrawable != null) {
             mDelegateDrawable.setColorFilter(colorFilter);
             return;
@@ -458,6 +460,7 @@
         invalidateSelf();
     }
 
+    @Nullable
     @Override
     public ColorFilter getColorFilter() {
         if (mDelegateDrawable != null) {
@@ -470,6 +473,7 @@
      * Ensures the tint filter is consistent with the current tint color and
      * mode.
      */
+    @SuppressWarnings("unused")
     PorterDuffColorFilter updateTintFilter(PorterDuffColorFilter tintFilter, ColorStateList tint,
                                            PorterDuff.Mode tintMode) {
         if (tint == null || tintMode == null) {
@@ -492,7 +496,7 @@
     }
 
     @Override
-    public void setTintList(ColorStateList tint) {
+    public void setTintList(@Nullable ColorStateList tint) {
         if (mDelegateDrawable != null) {
             DrawableCompat.setTintList(mDelegateDrawable, tint);
             return;
@@ -507,7 +511,7 @@
     }
 
     @Override
-    public void setTintMode(Mode tintMode) {
+    public void setTintMode(@Nullable Mode tintMode) {
         if (mDelegateDrawable != null) {
             DrawableCompat.setTintMode(mDelegateDrawable, tintMode);
             return;
@@ -611,6 +615,7 @@
      *
      * @hide
      */
+    @SuppressWarnings("unused")
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     public float getPixelSize() {
         if (mVectorState == null || mVectorState.mVPathRenderer == null
@@ -657,6 +662,7 @@
             @SuppressLint("ResourceType") final XmlPullParser parser = res.getXml(resId);
             final AttributeSet attrs = Xml.asAttributeSet(parser);
             int type;
+            //noinspection StatementWithEmptyBody
             while ((type = parser.next()) != XmlPullParser.START_TAG
                     && type != XmlPullParser.END_DOCUMENT) {
                 // Empty loop
@@ -679,8 +685,10 @@
      * document, tries to create a Drawable from that tag. Returns {@code null}
      * if the tag is not a valid drawable.
      */
-    public static VectorDrawableCompat createFromXmlInner(Resources r, XmlPullParser parser,
-            AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException {
+    @NonNull
+    public static VectorDrawableCompat createFromXmlInner(@NonNull Resources r,
+            @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
+            throws XmlPullParserException, IOException {
         final VectorDrawableCompat drawable = new VectorDrawableCompat();
         drawable.inflate(r, parser, attrs, theme);
         return drawable;
@@ -694,8 +702,8 @@
     }
 
     @Override
-    public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs)
-            throws XmlPullParserException, IOException {
+    public void inflate(@NonNull Resources res, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs) throws XmlPullParserException, IOException {
         if (mDelegateDrawable != null) {
             mDelegateDrawable.inflate(res, parser, attrs);
             return;
@@ -705,7 +713,8 @@
     }
 
     @Override
-    public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
+    public void inflate(@NonNull Resources res, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs, @Nullable Theme theme)
             throws XmlPullParserException, IOException {
         if (mDelegateDrawable != null) {
             DrawableCompat.inflate(mDelegateDrawable, res, parser, attrs, theme);
@@ -713,8 +722,7 @@
         }
 
         final VectorDrawableCompatState state = mVectorState;
-        final VPathRenderer pathRenderer = new VPathRenderer();
-        state.mVPathRenderer = pathRenderer;
+        state.mVPathRenderer = new VPathRenderer();
 
         final TypedArray a = TypedArrayUtils.obtainAttributes(res, theme, attrs,
                 AndroidResources.STYLEABLE_VECTOR_DRAWABLE_TYPE_ARRAY);
@@ -733,6 +741,7 @@
      * Parses a {@link android.graphics.PorterDuff.Mode} from a tintMode
      * attribute's enum value.
      */
+    @SuppressWarnings("SameParameterValue")
     private static PorterDuff.Mode parseTintModeCompat(int value, Mode defaultMode) {
         switch (value) {
             case 3:
@@ -834,33 +843,35 @@
             if (eventType == XmlPullParser.START_TAG) {
                 final String tagName = parser.getName();
                 final VGroup currentGroup = groupStack.peek();
-                if (SHAPE_PATH.equals(tagName)) {
-                    final VFullPath path = new VFullPath();
-                    path.inflate(res, attrs, theme, parser);
-                    currentGroup.mChildren.add(path);
-                    if (path.getPathName() != null) {
-                        pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
+                if (currentGroup != null) {
+                    if (SHAPE_PATH.equals(tagName)) {
+                        final VFullPath path = new VFullPath();
+                        path.inflate(res, attrs, theme, parser);
+                        currentGroup.mChildren.add(path);
+                        if (path.getPathName() != null) {
+                            pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
+                        }
+                        noPathTag = false;
+                        state.mChangingConfigurations |= path.mChangingConfigurations;
+                    } else if (SHAPE_CLIP_PATH.equals(tagName)) {
+                        final VClipPath path = new VClipPath();
+                        path.inflate(res, attrs, theme, parser);
+                        currentGroup.mChildren.add(path);
+                        if (path.getPathName() != null) {
+                            pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
+                        }
+                        state.mChangingConfigurations |= path.mChangingConfigurations;
+                    } else if (SHAPE_GROUP.equals(tagName)) {
+                        VGroup newChildGroup = new VGroup();
+                        newChildGroup.inflate(res, attrs, theme, parser);
+                        currentGroup.mChildren.add(newChildGroup);
+                        groupStack.push(newChildGroup);
+                        if (newChildGroup.getGroupName() != null) {
+                            pathRenderer.mVGTargetsMap.put(newChildGroup.getGroupName(),
+                                    newChildGroup);
+                        }
+                        state.mChangingConfigurations |= newChildGroup.mChangingConfigurations;
                     }
-                    noPathTag = false;
-                    state.mChangingConfigurations |= path.mChangingConfigurations;
-                } else if (SHAPE_CLIP_PATH.equals(tagName)) {
-                    final VClipPath path = new VClipPath();
-                    path.inflate(res, attrs, theme, parser);
-                    currentGroup.mChildren.add(path);
-                    if (path.getPathName() != null) {
-                        pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
-                    }
-                    state.mChangingConfigurations |= path.mChangingConfigurations;
-                } else if (SHAPE_GROUP.equals(tagName)) {
-                    VGroup newChildGroup = new VGroup();
-                    newChildGroup.inflate(res, attrs, theme, parser);
-                    currentGroup.mChildren.add(newChildGroup);
-                    groupStack.push(newChildGroup);
-                    if (newChildGroup.getGroupName() != null) {
-                        pathRenderer.mVGTargetsMap.put(newChildGroup.getGroupName(),
-                                newChildGroup);
-                    }
-                    state.mChangingConfigurations |= newChildGroup.mChangingConfigurations;
                 }
             } else if (eventType == XmlPullParser.END_TAG) {
                 final String tagName = parser.getName();
@@ -882,9 +893,9 @@
     }
 
     private void printGroupTree(VGroup currentGroup, int level) {
-        String indent = "";
+        StringBuilder indent = new StringBuilder();
         for (int i = 0; i < level; i++) {
-            indent += "    ";
+            indent.append("    ");
         }
         // Print the current node
         Log.v(LOGTAG, indent + "current group is :" + currentGroup.getGroupName()
@@ -901,6 +912,7 @@
         }
     }
 
+    @SuppressWarnings("SameParameterValue")
     void setAllowCaching(boolean allowCaching) {
         mAllowCaching = allowCaching;
     }
@@ -941,7 +953,7 @@
     }
 
     @Override
-    public void scheduleSelf(Runnable what, long when) {
+    public void scheduleSelf(@NonNull Runnable what, long when) {
         if (mDelegateDrawable != null) {
             mDelegateDrawable.scheduleSelf(what, when);
             return;
@@ -958,7 +970,7 @@
     }
 
     @Override
-    public void unscheduleSelf(Runnable what) {
+    public void unscheduleSelf(@NonNull Runnable what) {
         if (mDelegateDrawable != null) {
             mDelegateDrawable.unscheduleSelf(what);
             return;
@@ -1019,7 +1031,9 @@
         Mode mTintMode = DEFAULT_TINT_MODE;
         boolean mAutoMirrored;
 
+        // Cached fields, don't copy on mutate.
         Bitmap mCachedBitmap;
+        @SuppressWarnings("unused")
         int[] mCachedThemeAttrs;
         ColorStateList mCachedTint;
         Mode mCachedTintMode;
@@ -1033,6 +1047,7 @@
         Paint mTempPaint;
 
         // Deep copy for mutate() or implicitly mutate.
+        @SuppressWarnings("CopyConstructorMissesField") // Intentional, see field comments.
         VectorDrawableCompatState(VectorDrawableCompatState copy) {
             if (copy != null) {
                 mChangingConfigurations = copy.mChangingConfigurations;
@@ -1093,22 +1108,16 @@
         }
 
         public boolean canReuseBitmap(int width, int height) {
-            if (width == mCachedBitmap.getWidth()
-                    && height == mCachedBitmap.getHeight()) {
-                return true;
-            }
-            return false;
+            return width == mCachedBitmap.getWidth()
+                    && height == mCachedBitmap.getHeight();
         }
 
         public boolean canReuseCache() {
-            if (!mCacheDirty
+            return !mCacheDirty
                     && mCachedTint == mTint
                     && mCachedTintMode == mTintMode
                     && mCachedAutoMirrored == mAutoMirrored
-                    && mCachedRootAlpha == mVPathRenderer.getRootAlpha()) {
-                return true;
-            }
-            return false;
+                    && mCachedRootAlpha == mVPathRenderer.getRootAlpha();
         }
 
         public void updateCacheStates() {
@@ -1189,7 +1198,7 @@
         String mRootName = null;
         Boolean mIsStateful = null;
 
-        final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<String, Object>();
+        final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
 
         VPathRenderer() {
             mRootGroup = new VGroup();
@@ -1216,6 +1225,7 @@
             return getRootAlpha() / 255.0f;
         }
 
+        @SuppressWarnings("CopyConstructorMissesField")
         VPathRenderer(VPathRenderer copy) {
             mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
             mPath = new Path(copy.mPath);
@@ -1328,6 +1338,8 @@
                     final Paint fillPaint = mFillPaint;
                     if (fill.isGradient()) {
                         final Shader shader = fill.getShader();
+                        // isGradient() implies non-null shader
+                        //noinspection ConstantConditions
                         shader.setLocalMatrix(mFinalPathMatrix);
                         fillPaint.setShader(shader);
                         fillPaint.setAlpha(Math.round(fullPath.mFillAlpha * 255f));
@@ -1361,6 +1373,8 @@
                     strokePaint.setStrokeMiter(fullPath.mStrokeMiterlimit);
                     if (strokeColor.isGradient()) {
                         final Shader shader = strokeColor.getShader();
+                        // isGradient() implies non-null shader
+                        //noinspection ConstantConditions
                         shader.setLocalMatrix(mFinalPathMatrix);
                         strokePaint.setShader(shader);
                         strokePaint.setAlpha(Math.round(fullPath.mStrokeAlpha * 255f));
@@ -1698,9 +1712,9 @@
         }
 
         public void printVPath(int level) {
-            String indent = "";
+            StringBuilder indent = new StringBuilder();
             for (int i = 0; i < level; i++) {
-                indent += "    ";
+                indent.append("    ");
             }
             Log.v(LOGTAG, indent + "current path is :" + mPathName
                     + " pathData is " + nodesToString(mNodes));
@@ -1708,17 +1722,18 @@
         }
 
         public String nodesToString(PathParser.PathDataNode[] nodes) {
-            String result = " ";
-            for (int i = 0; i < nodes.length; i++) {
-                result += nodes[i].mType + ":";
-                float[] params = nodes[i].mParams;
-                for (int j = 0; j < params.length; j++) {
-                    result += params[j] + ",";
+            StringBuilder result = new StringBuilder(" ");
+            for (PathParser.PathDataNode node : nodes) {
+                result.append(node.mType).append(":");
+                float[] params = node.mParams;
+                for (float param : params) {
+                    result.append(param).append(",");
                 }
             }
-            return result;
+            return result.toString();
         }
 
+        @SuppressWarnings("CopyConstructorMissesField")
         VPath(VPath copy) {
             mPathName = copy.mPathName;
             mChangingConfigurations = copy.mChangingConfigurations;
@@ -1736,10 +1751,12 @@
             return mPathName;
         }
 
+        @SuppressWarnings("unused")
         public boolean canApplyTheme() {
             return false;
         }
 
+        @SuppressWarnings("unused")
         public void applyTheme(Theme t) {
         }
 
@@ -1972,10 +1989,6 @@
 
         @Override
         public void applyTheme(Theme t) {
-            if (mThemeAttrs == null) {
-                return;
-            }
-
             /*
              * TODO TINT THEME Not supported yet final TypedArray a =
              * t.resolveAttributes(mThemeAttrs, styleable_VectorDrawablePath);
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index a046124..5f02fa4 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -651,6 +651,8 @@
         }
     }
 
+    internal open fun cancelCoroutineScopesInOnDestroy() = true
+
     /**
      * This is open for use by tests, it allows them to inject a custom [SurfaceHolder].
      * @hide
@@ -1564,8 +1566,8 @@
             }
 
             // NB user code could throw an exception so do this last.
-            runBlocking {
-                try {
+            try {
+                runBlocking {
                     // The WatchFaceImpl is created on the UiThread so if we get here and it's not
                     // created we can be sure it'll never be created hence we don't need to destroy
                     // it.
@@ -1576,17 +1578,34 @@
                         watchFaceInitDetails
                             .await().watchFace.renderer.onDestroy()
                     }
-                } catch (e: Exception) {
-                    // Throwing an exception here leads to a cascade of errors, log instead.
-                    Log.e(
-                        TAG,
-                        "WatchFace exception observed in onDestroy (may have occurred during init)",
-                        e
-                    )
+                }
+            } catch (e: Exception) {
+                // Throwing an exception here leads to a cascade of errors, log instead.
+                Log.e(
+                    TAG,
+                    "WatchFace exception observed in onDestroy (may have occurred during init)",
+                    e
+                )
+            } finally {
+                if (this@EngineWrapper::ambientUpdateWakelock.isInitialized) {
+                    // Make sure the WakeLock doesn't retain the WatchFaceService.
+                    ambientUpdateWakelock.release()
+                }
+
+                // StateFlows may retain WatchFaceService via the coroutineScope. Call cancel to
+                // ensure resources are released. Headless watch faces call cancelCoroutineScopes
+                // themselves since they call onDestroy from a coroutine context.
+                if (cancelCoroutineScopesInOnDestroy() && !mutableWatchState.isHeadless) {
+                    cancelCoroutineScopes()
                 }
             }
         }
 
+        internal fun cancelCoroutineScopes() {
+            uiThreadCoroutineScope.cancel()
+            backgroundThreadCoroutineScope.cancel()
+        }
+
         override fun onSurfaceDestroyed(holder: SurfaceHolder) {
             surfaceDestroyed = true
         }
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/HeadlessWatchFaceImpl.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/HeadlessWatchFaceImpl.kt
index 337750d..f558093 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/HeadlessWatchFaceImpl.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/HeadlessWatchFaceImpl.kt
@@ -135,7 +135,8 @@
                         watchFaceService = null
                     }
                 }
-            }
+                engineCopy
+            }.cancelCoroutineScopes()
         }
     }
 }
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
index bad97a4..d550bab 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/TestCommon.kt
@@ -78,6 +78,12 @@
 
     override fun forceIsVisibleForTesting() = forceIsVisible
 
+    /**
+     * [WatchFaceService.EngineWrapper.onDestroy] is called more than once in some tests which is a
+     * problem due to using a CoroutineScope after it's been cancelled leading to exceptions.
+     */
+    override fun cancelCoroutineScopesInOnDestroy() = false
+
     fun reset() {
         clearTappedState()
         complicationSelected = null
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/foreground/SystemForegroundDispatcherTest.kt b/work/work-runtime/src/androidTest/java/androidx/work/impl/foreground/SystemForegroundDispatcherTest.kt
index 74e5983..3465172 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/foreground/SystemForegroundDispatcherTest.kt
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/foreground/SystemForegroundDispatcherTest.kt
@@ -28,9 +28,11 @@
 import androidx.work.Configuration
 import androidx.work.Constraints
 import androidx.work.ForegroundInfo
+import androidx.work.ListenableWorker
 import androidx.work.NetworkType
 import androidx.work.OneTimeWorkRequest
 import androidx.work.WorkInfo
+import androidx.work.WorkerParameters
 import androidx.work.impl.Processor
 import androidx.work.impl.Scheduler
 import androidx.work.impl.WorkDatabase
@@ -45,9 +47,11 @@
 import androidx.work.impl.foreground.SystemForegroundDispatcher.createStopForegroundIntent
 import androidx.work.impl.utils.StopWorkRunnable
 import androidx.work.impl.utils.SynchronousExecutor
+import androidx.work.impl.utils.futures.SettableFuture
 import androidx.work.impl.utils.taskexecutor.InstantWorkTaskExecutor
 import androidx.work.impl.utils.taskexecutor.TaskExecutor
 import androidx.work.worker.TestWorker
+import com.google.common.util.concurrent.ListenableFuture
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Before
@@ -127,11 +131,12 @@
 
     @Test
     fun testStartForeground_trackConstraints_workSpecHasConstraints() {
-        val request = OneTimeWorkRequest.Builder(TestWorker::class.java)
+        val request = OneTimeWorkRequest.Builder(NeverResolvedWorker::class.java)
             .setConstraints(
                 Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
             ).build()
         workDatabase.workSpecDao().insertWorkSpec(request.workSpec)
+        processor.startWork(StartStopToken(WorkGenerationalId(request.stringId, 0)))
         val notificationId = 1
         val notification = mock(Notification::class.java)
         val metadata = ForegroundInfo(notificationId, notification)
@@ -338,11 +343,12 @@
 
     @Test
     fun testStartForeground_trackConstraints_constraintsUnMet() {
-        val request = OneTimeWorkRequest.Builder(TestWorker::class.java)
+        val request = OneTimeWorkRequest.Builder(NeverResolvedWorker::class.java)
             .setConstraints(
                 Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
             ).build()
         workDatabase.workSpecDao().insertWorkSpec(request.workSpec)
+        processor.startWork(StartStopToken(WorkGenerationalId(request.stringId, 0)))
         val notificationId = 1
         val notification = mock(Notification::class.java)
         val metadata = ForegroundInfo(notificationId, notification)
@@ -358,11 +364,12 @@
 
     @Test
     fun testCancelForegroundWork() {
-        val request = OneTimeWorkRequest.Builder(TestWorker::class.java)
+        val request = OneTimeWorkRequest.Builder(NeverResolvedWorker::class.java)
             .setConstraints(
                 Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
             ).build()
         workDatabase.workSpecDao().insertWorkSpec(request.workSpec)
+        processor.startWork(StartStopToken(WorkGenerationalId(request.stringId, 0)))
         val notificationId = 1
         val notification = mock(Notification::class.java)
         val metadata = ForegroundInfo(notificationId, notification)
@@ -401,4 +408,33 @@
         verify(workManager, times(1)).cancelWorkById(eq(UUID.fromString(request.workSpec.id)))
         assertThat(processor.hasWork(), `is`(false))
     }
+
+    @Test
+    fun testUseRunningWork() {
+        val request = OneTimeWorkRequest.Builder(NeverResolvedWorker::class.java)
+            .setConstraints(Constraints(requiredNetworkType = NetworkType.CONNECTED))
+            .build()
+        workDatabase.workSpecDao().insertWorkSpec(request.workSpec)
+        processor.startWork(StartStopToken(WorkGenerationalId(request.stringId, 0)))
+        val updatedRequest = OneTimeWorkRequest.Builder(NeverResolvedWorker::class.java)
+            .setId(request.id)
+            .build()
+        workDatabase.workSpecDao().updateWorkSpec(updatedRequest.workSpec)
+        val notificationId = 1
+        val notification = mock(Notification::class.java)
+        val metadata = ForegroundInfo(notificationId, notification)
+        val intent = createStartForegroundIntent(context,
+            WorkGenerationalId(request.stringId, 0), metadata)
+        dispatcher.onStartCommand(intent)
+        verify(tracker, times(1)).replace(setOf(request.workSpec))
+    }
+}
+
+class NeverResolvedWorker(
+    context: Context,
+    workerParams: WorkerParameters
+) : ListenableWorker(context, workerParams) {
+    override fun startWork(): ListenableFuture<Result> {
+        return SettableFuture.create()
+    }
 }
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/Processor.java b/work/work-runtime/src/main/java/androidx/work/impl/Processor.java
index d0f1479..5efb6a1 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/Processor.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/Processor.java
@@ -382,6 +382,26 @@
         }
     }
 
+    /**
+     * Returns a spec of the running worker by the given id
+     *
+     * @param workSpecId id of running worker
+     */
+    @Nullable
+    public WorkSpec getRunningWorkSpec(@NonNull String workSpecId) {
+        synchronized (mLock) {
+            WorkerWrapper workerWrapper = mForegroundWorkMap.get(workSpecId);
+            if (workerWrapper == null) {
+                workerWrapper = mEnqueuedWorkMap.get(workSpecId);
+            }
+            if (workerWrapper != null) {
+                return workerWrapper.getWorkSpec();
+            } else {
+                return null;
+            }
+        }
+    }
+
     private void runOnExecuted(@NonNull final WorkGenerationalId id, boolean needsReschedule) {
         mWorkTaskExecutor.getMainThreadExecutor().execute(
                 () -> onExecuted(id, needsReschedule)
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/WorkerWrapper.java b/work/work-runtime/src/main/java/androidx/work/impl/WorkerWrapper.java
index 78f014f..d5ad905 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/WorkerWrapper.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/WorkerWrapper.java
@@ -148,6 +148,11 @@
         runWorker();
     }
 
+    @NonNull
+    public WorkSpec getWorkSpec() {
+        return mWorkSpec;
+    }
+
     private void runWorker() {
         if (tryCheckForInterruptionAndResolve()) {
             return;
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundDispatcher.java b/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundDispatcher.java
index e2a9335..4890888 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundDispatcher.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundDispatcher.java
@@ -35,7 +35,6 @@
 import androidx.work.ForegroundInfo;
 import androidx.work.Logger;
 import androidx.work.impl.ExecutionListener;
-import androidx.work.impl.WorkDatabase;
 import androidx.work.impl.WorkManagerImpl;
 import androidx.work.impl.constraints.WorkConstraintsCallback;
 import androidx.work.impl.constraints.WorkConstraintsTracker;
@@ -239,11 +238,10 @@
     private void handleStartForeground(@NonNull Intent intent) {
         Logger.get().info(TAG, "Started foreground service " + intent);
         final String workSpecId = intent.getStringExtra(KEY_WORKSPEC_ID);
-        final WorkDatabase database = mWorkManagerImpl.getWorkDatabase();
         mTaskExecutor.executeOnTaskThread(new Runnable() {
             @Override
             public void run() {
-                WorkSpec workSpec = database.workSpecDao().getWorkSpec(workSpecId);
+                WorkSpec workSpec = mWorkManagerImpl.getProcessor().getRunningWorkSpec(workSpecId);
                 // Only track constraints if there are constraints that need to be tracked
                 // (constraints are immutable)
                 if (workSpec != null && workSpec.hasConstraints()) {