[go: nahoru, domu]

Merge "Update Kotlinpoet to 1.14.2" into androidx-main
diff --git a/appcompat/appcompat/src/androidTest/AndroidManifest.xml b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
index fb17a4b..aa16fad 100644
--- a/appcompat/appcompat/src/androidTest/AndroidManifest.xml
+++ b/appcompat/appcompat/src/androidTest/AndroidManifest.xml
@@ -131,6 +131,11 @@
             android:theme="@style/Theme.TextColors"/>
 
         <activity
+            android:name="androidx.appcompat.widget.AppCompatTextViewFontScalingTest$TextViewFontScalingActivity"
+            android:label="@string/app_compat_text_view_activity"
+            android:theme="@style/Theme.TextColors" />
+
+        <activity
             android:name="androidx.appcompat.widget.AppCompatTextViewAutoSizeActivity"
             android:label="@string/app_compat_text_view_auto_size_activity"
             android:theme="@style/Theme.AppCompat.Light"/>
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewFontScalingTest.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewFontScalingTest.kt
new file mode 100644
index 0000000..a87d598
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewFontScalingTest.kt
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appcompat.widget
+
+import android.app.Activity
+import android.os.Build
+import android.os.Bundle
+import android.util.TypedValue
+import android.widget.TextView
+import androidx.appcompat.test.R
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.testutils.AndroidFontScaleHelper.resetSystemFontScale
+import androidx.testutils.AndroidFontScaleHelper.setSystemFontScale
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Test {@link AppCompatTextView} under non-linear font scaling.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class AppCompatTextViewFontScalingTest {
+    @get:Rule
+    val scenarioRule = ActivityScenarioRule(TextViewFontScalingActivity::class.java)
+
+    @After
+    fun teardown() {
+        // Have to manually check the version here because if we try to rely on the assumeTrue() in
+        // resetSystemFontScale(), it is called twice (again in setSystemFontScale()) and the test
+        // fails with a "TestCouldNotBeSkippedException: Test could not be skipped due to other
+        // failures" because it thinks the second assumeTrue() was a separate error.
+        // tl;dr avoids a bug in jUnit when multiple assumeTrue()s happen in @Test and @After
+        if (Build.VERSION.SDK_INT >= 29) {
+            resetSystemFontScale(scenarioRule.scenario)
+        }
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun testNonLinearFontScaling_testSetLineHeightSpAndSetTextSizeSp() {
+        setSystemFontScale(2f, scenarioRule.scenario)
+        scenarioRule.scenario.onActivity { activity ->
+            assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
+
+            val textView = AppCompatTextView(activity)
+            val textSizeSp = 20f
+            val lineHeightSp = 40f
+
+            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSizeSp)
+            textView.setLineHeight(TypedValue.COMPLEX_UNIT_SP, lineHeightSp)
+
+            verifyLineHeightIsIntendedProportions(lineHeightSp, textSizeSp, activity, textView)
+        }
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun testNonLinearFontScaling_overwriteXml_testSetLineHeightSpAndSetTextSizeSp() {
+        setSystemFontScale(2f, scenarioRule.scenario)
+        scenarioRule.scenario.onActivity { activity ->
+            assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
+
+            val textView = findTextView(activity, R.id.textview_lineheight2x)
+            val textSizeSp = 20f
+            val lineHeightSp = 40f
+
+            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSizeSp)
+            textView.setLineHeight(TypedValue.COMPLEX_UNIT_SP, lineHeightSp)
+
+            verifyLineHeightIsIntendedProportions(lineHeightSp, textSizeSp, activity, textView)
+        }
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun testNonLinearFontScaling_xml_testLineHeightAttrSpAndTextSizeAttrSp() {
+        setSystemFontScale(2f, scenarioRule.scenario)
+        scenarioRule.scenario.onActivity { activity ->
+            assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
+
+            val textView = findTextView(activity, R.id.textview_lineheight2x)
+            val textSizeSp = 20f
+            val lineHeightSp = 40f
+
+            verifyLineHeightIsIntendedProportions(lineHeightSp, textSizeSp, activity, textView)
+        }
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun testNonLinearFontScaling_dimenXml_testLineHeightAttrSpAndTextSizeAttrSp() {
+        setSystemFontScale(2f, scenarioRule.scenario)
+        scenarioRule.scenario.onActivity { activity ->
+            assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
+
+            val textView = findTextView(activity, R.id.textview_lineheight_dimen3x)
+            val textSizeSp = 20f
+            val lineHeightSp = 60f
+
+            verifyLineHeightIsIntendedProportions(lineHeightSp, textSizeSp, activity, textView)
+        }
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun testNonLinearFontScaling_styleXml_testLineHeightAttrSpAndTextSizeAttrSp() {
+        setSystemFontScale(2f, scenarioRule.scenario)
+        scenarioRule.scenario.onActivity { activity ->
+            assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
+
+            val textView = findTextView(activity, R.id.textview_lineheight_style3x)
+            val textSizeSp = 20f
+            val lineHeightSp = 60f
+
+            verifyLineHeightIsIntendedProportions(lineHeightSp, textSizeSp, activity, textView)
+        }
+    }
+
+    @Test
+    @Throws(Throwable::class)
+    fun testNonLinearFontScaling_dimenXml_testSetLineHeightSpAndTextSizeAttrSp() {
+        setSystemFontScale(2f, scenarioRule.scenario)
+        scenarioRule.scenario.onActivity { activity ->
+            assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
+
+            val textView = findTextView(activity, R.id.textview_lineheight_dimen3x)
+            val textSizeSp = 20f
+            val lineHeightSp = 30f
+
+            textView.setLineHeight(TypedValue.COMPLEX_UNIT_SP, lineHeightSp)
+
+            verifyLineHeightIsIntendedProportions(lineHeightSp, textSizeSp, activity, textView)
+        }
+    }
+
+    private fun findTextView(activity: Activity, id: Int): AppCompatTextView {
+        return activity.findViewById(id)!!
+    }
+
+    class TextViewFontScalingActivity : Activity() {
+        override fun onCreate(savedInstanceState: Bundle?) {
+            super.onCreate(savedInstanceState)
+            setContentView(R.layout.appcompat_textview_fontscaling_activity)
+        }
+    }
+
+    companion object {
+        /**
+         * Tolerance for comparing expected float lineHeight to the integer one returned by
+         * getLineHeight(). It is pretty lenient to account for integer rounding when text size is
+         * loaded from an attribute. (When loading an SP resource from an attribute for textSize,
+         * it is rounded to the nearest pixel, which can throw off calculations quite a lot. Not
+         * enough to make much of a difference to the user, but enough to need a wide tolerance in
+         * tests. See b/279456702 for more details.)
+         */
+        private const val TOLERANCE = 5f
+
+        private fun verifyLineHeightIsIntendedProportions(
+            lineHeightSp: Float,
+            textSizeSp: Float,
+            activity: Activity,
+            textView: TextView
+        ) {
+            val lineHeightMultiplier = lineHeightSp / textSizeSp
+            // Calculate what line height would be without non-linear font scaling compressing it.
+            // The trick is multiplying afterwards (by the pixel value) instead of before (by the SP
+            // value)
+            val expectedLineHeightPx = lineHeightMultiplier * TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_SP,
+                textSizeSp,
+                activity.resources.displayMetrics
+            )
+            assertThat(textView.lineHeight.toFloat())
+                .isWithin(TOLERANCE)
+                .of(expectedLineHeightPx)
+        }
+    }
+}
diff --git a/appcompat/appcompat/src/androidTest/res/layout/appcompat_textview_fontscaling_activity.xml b/appcompat/appcompat/src/androidTest/res/layout/appcompat_textview_fontscaling_activity.xml
new file mode 100644
index 0000000..fadabf4
--- /dev/null
+++ b/appcompat/appcompat/src/androidTest/res/layout/appcompat_textview_fontscaling_activity.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:id="@+id/layout_textviewtest"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <!-- Tests line height 2x the text size -->
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/textview_lineheight2x"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/sample_text_long"
+            android:textSize="20sp"
+            android:lineHeight="40sp" />
+
+        <!-- Tests line height 3x the text size -->
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/textview_lineheight_dimen3x"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/sample_text_long"
+            android:textSize="@dimen/textview_fontScaling_textSize"
+            android:lineHeight="@dimen/textview_fontScaling_lineHeight" />
+
+        <!-- Tests line height 3x the text size -->
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/textview_lineheight_style3x"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/sample_text_long"
+            style="@style/TextAppearance.FontScaling" />
+
+    </LinearLayout>
+
+</ScrollView>
diff --git a/appcompat/appcompat/src/androidTest/res/values/dimens.xml b/appcompat/appcompat/src/androidTest/res/values/dimens.xml
index d4a8a0e..aed0b5d 100644
--- a/appcompat/appcompat/src/androidTest/res/values/dimens.xml
+++ b/appcompat/appcompat/src/androidTest/res/values/dimens.xml
@@ -27,4 +27,7 @@
     <dimen name="textview_firstBaselineToTopHeight">100dp</dimen>
     <dimen name="textview_lastBaselineToBottomHeight">30dp</dimen>
     <dimen name="textview_lineHeight">60dp</dimen>
+
+    <dimen name="textview_fontScaling_textSize">20sp</dimen>
+    <dimen name="textview_fontScaling_lineHeight">60sp</dimen>
 </resources>
\ No newline at end of file
diff --git a/appcompat/appcompat/src/androidTest/res/values/donottranslate-strings.xml b/appcompat/appcompat/src/androidTest/res/values/donottranslate-strings.xml
index 151400a..7a07538 100644
--- a/appcompat/appcompat/src/androidTest/res/values/donottranslate-strings.xml
+++ b/appcompat/appcompat/src/androidTest/res/values/donottranslate-strings.xml
@@ -71,6 +71,16 @@
     <string name="app_compat_button_auto_size_activity">AppCompat button auto-size</string>
     <string name="sample_text1">Sample text 1</string>
     <string name="sample_text2">Sample text 2</string>
+    <string name="sample_text_long">This is a really long string which exceeds the width of the
+view. New devices have a much larger screen which actually enables long strings to be displayed
+with no fading. I have made this string longer to fix this case. If you are correcting this
+text, I would love to see the kind of devices you guys now use! Guys, maybe some devices need longer
+string! I think so, so how about double this string, like copy and paste!
+This is a really long string which exceeds the width of the view.
+New devices have a much larger screen which actually enables long strings to be displayed
+with no fading. I have made this string longer to fix this case. If you are correcting this
+text, I would love to see the kind of devices you guys now use! Guys, maybe some devices need longer
+string! I think so, so how about double this string, like copy and paste!</string>
     <string name="app_compat_image_button_activity">AppCompat image button</string>
     <string name="app_compat_image_view_activity">AppCompat image view</string>
     <string name="app_compat_button_activity">AppCompat button</string>
diff --git a/appcompat/appcompat/src/androidTest/res/values/styles.xml b/appcompat/appcompat/src/androidTest/res/values/styles.xml
index 0b540bf..a076019 100644
--- a/appcompat/appcompat/src/androidTest/res/values/styles.xml
+++ b/appcompat/appcompat/src/androidTest/res/values/styles.xml
@@ -146,4 +146,9 @@
         <item name="android:colorForeground">@color/color_state_list_lilac</item>
     </style>
 
+    <!-- Tests line height 3x the text size -->
+    <style name="TextAppearance.FontScaling">
+        <item name="android:textSize">@dimen/textview_fontScaling_textSize</item>
+        <item name="android:lineHeight">@dimen/textview_fontScaling_lineHeight</item>
+    </style>
 </resources>
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
index fda4335..4eb3371 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
@@ -42,6 +42,7 @@
 import androidx.annotation.RestrictTo;
 import androidx.appcompat.R;
 import androidx.core.content.res.ResourcesCompat;
+import androidx.core.util.TypedValueCompat;
 import androidx.core.view.ViewCompat;
 import androidx.core.view.inputmethod.EditorInfoCompat;
 import androidx.core.widget.TextViewCompat;
@@ -326,8 +327,20 @@
                 R.styleable.AppCompatTextView_firstBaselineToTopHeight, -1);
         final int lastBaselineToBottomHeight = a.getDimensionPixelSize(
                 R.styleable.AppCompatTextView_lastBaselineToBottomHeight, -1);
-        final int lineHeight = a.getDimensionPixelSize(
-                R.styleable.AppCompatTextView_lineHeight, -1);
+        float lineHeight = -1;
+        int lineHeightUnit = -1;
+        if (a.hasValue(R.styleable.AppCompatTextView_lineHeight)) {
+            TypedValue peekValue = a.peekValue(R.styleable.AppCompatTextView_lineHeight);
+            if (peekValue != null && peekValue.type == TypedValue.TYPE_DIMENSION) {
+                lineHeightUnit = TypedValueCompat.getUnitFromComplexDimension(peekValue.data);
+                lineHeight = TypedValue.complexToFloat(peekValue.data);
+            } else {
+                lineHeight = a.getDimensionPixelSize(
+                        R.styleable.AppCompatTextView_lineHeight,
+                        -1
+                );
+            }
+        }
 
         a.recycle();
         if (firstBaselineToTopHeight != -1) {
@@ -337,7 +350,11 @@
             TextViewCompat.setLastBaselineToBottomHeight(mView, lastBaselineToBottomHeight);
         }
         if (lineHeight != -1) {
-            TextViewCompat.setLineHeight(mView, lineHeight);
+            if (lineHeightUnit == -1) {
+                TextViewCompat.setLineHeight(mView, (int) lineHeight);
+            } else {
+                TextViewCompat.setLineHeight(mView, lineHeightUnit, lineHeight);
+            }
         }
     }
 
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
index 1e92e4a..1f0e15a 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
@@ -37,6 +37,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.DrawableRes;
+import androidx.annotation.FloatRange;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -477,6 +478,15 @@
         TextViewCompat.setLineHeight(this, lineHeight);
     }
 
+    @Override
+    public void setLineHeight(int unit, @FloatRange(from = 0) float lineHeight) {
+        if (Build.VERSION.SDK_INT >= 34) {
+            getSuperCaller().setLineHeight(unit, lineHeight);
+        } else {
+            TextViewCompat.setLineHeight(this, unit, lineHeight);
+        }
+    }
+
     /**
      * See
      * {@link TextViewCompat#setCustomSelectionActionModeCallback(TextView, ActionMode.Callback)}
@@ -789,7 +799,9 @@
     @RequiresApi(api = 26)
     SuperCaller getSuperCaller() {
         if (mSuperCaller == null) {
-            if (Build.VERSION.SDK_INT >= 28) {
+            if (Build.VERSION.SDK_INT >= 34) {
+                mSuperCaller = new SuperCallerApi34();
+            } else if (Build.VERSION.SDK_INT >= 28) {
                 mSuperCaller = new SuperCallerApi28();
             } else if (Build.VERSION.SDK_INT >= 26) {
                 mSuperCaller = new SuperCallerApi26();
@@ -817,6 +829,9 @@
         // api 28
         void setFirstBaselineToTopHeight(@Px int firstBaselineToTopHeight);
         void setLastBaselineToBottomHeight(@Px int lastBaselineToBottomHeight);
+
+        // api 34
+        void setLineHeight(int unit, @FloatRange(from = 0) float lineHeight);
     }
 
     @RequiresApi(api = 26)
@@ -878,6 +893,9 @@
 
         @Override
         public void setLastBaselineToBottomHeight(int lastBaselineToBottomHeight) {}
+
+        @Override
+        public void setLineHeight(int unit, float lineHeight) {}
     }
 
     @RequiresApi(api = 28)
@@ -893,4 +911,12 @@
             AppCompatTextView.super.setLastBaselineToBottomHeight(lastBaselineToBottomHeight);
         }
     }
+
+    @RequiresApi(api = 34)
+    class SuperCallerApi34 extends SuperCallerApi28 {
+        @Override
+        public void setLineHeight(int unit, float lineHeight) {
+            AppCompatTextView.super.setLineHeight(unit, lineHeight);
+        }
+    }
 }
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
index cbfeea9..de56b69 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
@@ -645,6 +645,8 @@
                         PrefixUtil.removePrefix(grouping2.getEntryGroupings(1).getNamespace()));
     }
 
+    // @exportToFramework:startStrip()
+    // TODO(b/258715421) start exporting this when it is unhidden in framework
     @Test
     public void testToResultSpecProto_groupBySchema() throws Exception {
         SearchSpec searchSpec = new SearchSpec.Builder()
@@ -691,6 +693,7 @@
                 .isEqualTo(
                     PrefixUtil.removePrefix(grouping2.getEntryGroupings(1).getSchema()));
     }
+    // @exportToFramework:endStrip()
 
     @Test
     public void testToResultSpecProto_groupByNamespaceAndPackage() throws Exception {
@@ -727,6 +730,8 @@
         assertThat(resultSpecProto.getResultGroupings(3).getEntryGroupingsList()).hasSize(1);
     }
 
+    // @exportToFramework:startStrip()
+    // TODO(b/258715421) start exporting this when it is unhidden in framework
     @Test
     public void testToResultSpecProto_groupBySchemaAndPackage() throws Exception {
         SearchSpec searchSpec = new SearchSpec.Builder()
@@ -964,6 +969,7 @@
         assertThat(grouping8.getEntryGroupings(0).getSchema())
                 .isEqualTo("package1$database/typeB");
     }
+    // @exportToFramework:endStrip()
 
     @Test
     public void testGetTargetNamespaceFilters_emptySearchingFilter() {
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
index 794ed1f..ed3a8f6 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
@@ -63,7 +63,7 @@
 
             // Beyond Android U features
             case Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA:
-                // TODO(b/258715421): Update to reflect support in Android U+ once this feature has
+                // TODO(b/258715421) : Update to reflect support in Android U+ once this feature has
                 // an extservices sdk that includes it.
                 // fall through
             case Features.SCHEMA_SET_DELETION_PROPAGATION:
diff --git a/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt b/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt
index f53eb4f..e4d7736 100644
--- a/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt
+++ b/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt
@@ -39,7 +39,7 @@
     fun init() {
         builder = ConfigBuilder()
         builder.configName("placeHolderAndroidTest.xml")
-            .isBenchmark(false)
+            .isMicrobenchmark(false)
             .applicationId("com.androidx.placeholder.Placeholder")
             .isPostsubmit(true)
             .minSdk("15")
@@ -59,7 +59,7 @@
 
     @Test
     fun testXmlAgainstGoldenDefaultBenchmark() {
-        builder.isBenchmark(true)
+        builder.isMicrobenchmark(true)
         MatcherAssert.assertThat(
             builder.buildXml(),
             CoreMatchers.`is`(goldenDefaultConfigBenchmark)
@@ -123,7 +123,7 @@
 
     @Test
     fun testJsonAgainstGoldenPresubmitBenchmark() {
-        builder.isBenchmark(true)
+        builder.isMicrobenchmark(true)
             .isPostsubmit(false)
         MatcherAssert.assertThat(
             builder.buildJson(),
@@ -241,7 +241,7 @@
 
     @Test
     fun testValidTestConfigXml_benchmarkTrue() {
-        builder.isBenchmark(true)
+        builder.isMicrobenchmark(true)
         validate(builder.buildXml())
     }
 
@@ -279,7 +279,7 @@
     @Test
     fun testValidTestConfigXml_presubmitBenchmark() {
         builder.isPostsubmit(false)
-            .isBenchmark(true)
+            .isMicrobenchmark(true)
         validate(builder.buildXml())
     }
 
@@ -349,7 +349,6 @@
     <option name="config-descriptor:metadata" key="applicationId" value="com.androidx.placeholder.Placeholder" />
     <option name="wifi:disable" value="true" />
     <option name="instrumentation-arg" key="notAnnotation" value="androidx.test.filters.FlakyTest" />
-    <option name="instrumentation-arg" key="androidx.benchmark.output.enable" value="true" />
     <option name="instrumentation-arg" key="listener" value="androidx.benchmark.junit4.InstrumentationResultsRunListener" />
     <include name="google/unbundled/common/setup" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index 2b8e4e7..2c51b04 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -162,6 +162,7 @@
                 project.validatePublishedMultiplatformHasDefault()
             }
         }
+        project.disallowAccidentalAndroidDependenciesInKmpProject(kmpExtension)
     }
 
     private fun Project.registerProjectOrArtifact() {
@@ -931,13 +932,6 @@
             return
         }
         project.afterEvaluate {
-            if (project.hasKotlinNativeTarget().get()) {
-                // KMP plugin cannot handle constraints properly for native targets
-                // b/274786186, YT: KT-57531
-                // It is expected to be fixed in Kotlin 1.9 after which, we should remove this check
-                return@afterEvaluate
-            }
-
             // make sure that the project has a group
             val projectGroup = extension.mavenGroup ?: return@afterEvaluate
             // make sure that this group is configured to use a single version
@@ -1221,6 +1215,31 @@
     }
 }
 
+/**
+ * Verifies we don't accidentially write "implementation" instead of "commonMainImplementation"
+ */
+fun Project.disallowAccidentalAndroidDependenciesInKmpProject(
+    kmpExtension: AndroidXMultiplatformExtension
+) {
+    project.afterEvaluate {
+        if (kmpExtension.supportedPlatforms.isNotEmpty()) {
+            val androidConfiguration = project.configurations.findByName("implementation")
+            if (androidConfiguration != null) {
+               if (
+                   androidConfiguration.dependencies.isNotEmpty() ||
+                   androidConfiguration.dependencyConstraints.isNotEmpty()
+               ) {
+                   throw GradleException(
+                       "The 'implementation' Configuration should not be used in a " +
+                       "multiplatform project: this Configuration is declared by the " +
+                       "Android plugin rather than the kmp plugin. Did you mean " +
+                       "'commonMainImplementation'?")
+                }
+            }
+        }
+    }
+}
+
 /** Verifies that ProjectParser computes the correct values for this project */
 fun Project.validateProjectParser(extension: AndroidXExtension) {
     // If configuration fails, we don't want to validate the ProjectParser
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
index 86d3217..a3fd34e 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
@@ -50,16 +50,14 @@
     private val kotlinExtension: KotlinMultiplatformExtension by kotlinExtensionDelegate
 
     /**
-     * The list of platforms that have been requested in the build configuration.
+     * The list of platforms that have been declared as supported in the build configuration.
      *
-     * The list of enabled platforms in [targetPlatforms] will vary based on the build environment.
-     * For example, a project's build configuration may have requested `mac()` but this is not
-     * available when building on Linux.
+     * This may be a superset of the currently enabled platforms in [targetPlatforms].
      */
-    val requestedPlatforms: MutableSet<PlatformIdentifier> = mutableSetOf()
+    val supportedPlatforms: MutableSet<PlatformIdentifier> = mutableSetOf()
 
     /**
-     * The list of platforms that are enabled.
+     * The list of platforms that are currently enabled.
      *
      * This will vary across build environments. For example, a project's build configuration may
      * have requested `mac()` but this is not available when building on Linux.
@@ -91,14 +89,14 @@
      * identifier for that platform.
      */
     var defaultPlatform: String? = null
-        get() = field ?: requestedPlatforms.singleOrNull()?.id
+        get() = field ?: supportedPlatforms.singleOrNull()?.id
         set(value) {
             if (value != null) {
-                if (requestedPlatforms.none { it.id == value }) {
+                if (supportedPlatforms.none { it.id == value }) {
                     throw GradleException(
                         "Platform $value has not been requested as a target. " +
                             "Available platforms are: " +
-                            requestedPlatforms.joinToString(", ") { it.id }
+                            supportedPlatforms.joinToString(", ") { it.id }
                     )
                 }
                 if (targetPlatforms.none { it == value }) {
@@ -145,7 +143,7 @@
 
     @JvmOverloads
     fun jvm(block: Action<KotlinJvmTarget>? = null): KotlinJvmTarget? {
-        requestedPlatforms.add(PlatformIdentifier.JVM)
+        supportedPlatforms.add(PlatformIdentifier.JVM)
         return if (project.enableJvm()) {
             kotlinExtension.jvm {
                 block?.execute(this)
@@ -163,7 +161,7 @@
 
     @JvmOverloads
     fun android(block: Action<KotlinAndroidTarget>? = null): KotlinAndroidTarget? {
-        requestedPlatforms.add(PlatformIdentifier.ANDROID)
+        supportedPlatforms.add(PlatformIdentifier.ANDROID)
         return if (project.enableJvm()) {
             kotlinExtension.androidTarget { block?.execute(this) }
         } else {
@@ -173,7 +171,7 @@
 
     @JvmOverloads
     fun desktop(block: Action<KotlinJvmTarget>? = null): KotlinJvmTarget? {
-        requestedPlatforms.add(PlatformIdentifier.DESKTOP)
+        supportedPlatforms.add(PlatformIdentifier.DESKTOP)
         return if (project.enableDesktop()) {
             kotlinExtension.jvm("desktop") { block?.execute(this) }
         } else {
@@ -189,7 +187,7 @@
 
     @JvmOverloads
     fun macosX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTargetWithHostTests? {
-        requestedPlatforms.add(PlatformIdentifier.MAC_OSX_64)
+        supportedPlatforms.add(PlatformIdentifier.MAC_OSX_64)
         return if (project.enableMac()) {
             kotlinExtension.macosX64().also { block?.execute(it) }
         } else {
@@ -199,7 +197,7 @@
 
     @JvmOverloads
     fun macosArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTargetWithHostTests? {
-        requestedPlatforms.add(PlatformIdentifier.MAC_ARM_64)
+        supportedPlatforms.add(PlatformIdentifier.MAC_ARM_64)
         return if (project.enableMac()) {
             kotlinExtension.macosArm64().also { block?.execute(it) }
         } else {
@@ -209,7 +207,7 @@
 
     @JvmOverloads
     fun iosArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
-        requestedPlatforms.add(PlatformIdentifier.IOS_ARM_64)
+        supportedPlatforms.add(PlatformIdentifier.IOS_ARM_64)
         return if (project.enableMac()) {
             kotlinExtension.iosArm64().also { block?.execute(it) }
         } else {
@@ -225,7 +223,7 @@
 
     @JvmOverloads
     fun iosX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
-        requestedPlatforms.add(PlatformIdentifier.IOS_X_64)
+        supportedPlatforms.add(PlatformIdentifier.IOS_X_64)
         return if (project.enableMac()) {
             kotlinExtension.iosX64().also { block?.execute(it) }
         } else {
@@ -235,7 +233,7 @@
 
     @JvmOverloads
     fun iosSimulatorArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
-        requestedPlatforms.add(PlatformIdentifier.IOS_SIMULATOR_ARM_64)
+        supportedPlatforms.add(PlatformIdentifier.IOS_SIMULATOR_ARM_64)
         return if (project.enableMac()) {
             kotlinExtension.iosSimulatorArm64().also { block?.execute(it) }
         } else {
@@ -252,7 +250,7 @@
 
     @JvmOverloads
     fun linuxX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTargetWithHostTests? {
-        requestedPlatforms.add(PlatformIdentifier.LINUX_64)
+        supportedPlatforms.add(PlatformIdentifier.LINUX_64)
         return if (project.enableLinux()) {
             kotlinExtension.linuxX64().also { block?.execute(it) }
         } else {
@@ -262,7 +260,7 @@
 
     @JvmOverloads
     fun js(block: Action<KotlinJsTargetDsl>? = null): KotlinJsTargetDsl? {
-        requestedPlatforms.add(PlatformIdentifier.JS)
+        supportedPlatforms.add(PlatformIdentifier.JS)
         return if (project.enableJs()) {
             kotlinExtension.js().also { block?.execute(it) }
         } else {
@@ -286,7 +284,7 @@
 
 fun Project.validatePublishedMultiplatformHasDefault() {
     val extension = project.extensions.getByType(AndroidXMultiplatformExtension::class.java)
-    if (extension.defaultPlatform == null && extension.requestedPlatforms.isNotEmpty()) {
+    if (extension.defaultPlatform == null && extension.supportedPlatforms.isNotEmpty()) {
         throw GradleException(
             "Project is published and multiple platforms are requested. You " +
                 "must explicitly specify androidXMultiplatform.defaultPlatform as one of: " +
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt b/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
index 9265e5c..724504f 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
@@ -566,10 +566,6 @@
                     ":media2:media2-session:version-compat-tests:service",
                     ":media2:media2-session:version-compat-tests:client-previous",
                     ":media2:media2-session:version-compat-tests:service-previous"
-                ), // Link graphics and material to always run @Large in presubmit per b/160624022
-                setOf(
-                    ":compose:ui:ui-graphics",
-                    ":compose:material:material"
                 ), // Link material and material-ripple
                 setOf(":compose:material:material-ripple", ":compose:material:material"),
                 setOf(
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt
index a3c6691..657f702 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt
@@ -23,7 +23,7 @@
     var appApkName: String? = null
     var appApkSha256: String? = null
     lateinit var applicationId: String
-    var isBenchmark: Boolean = false
+    var isMicrobenchmark: Boolean = false
     var isPostsubmit: Boolean = true
     lateinit var minSdk: String
     val tags = mutableListOf<String>()
@@ -40,7 +40,8 @@
 
     fun applicationId(applicationId: String) = apply { this.applicationId = applicationId }
 
-    fun isBenchmark(isBenchmark: Boolean) = apply { this.isBenchmark = isBenchmark }
+    fun isMicrobenchmark(isMicrobenchmark: Boolean) =
+        apply { this.isMicrobenchmark = isMicrobenchmark }
 
     fun isPostsubmit(isPostsubmit: Boolean) = apply { this.isPostsubmit = isPostsubmit }
 
@@ -59,7 +60,7 @@
     fun buildJson(): String {
         val gson = GsonBuilder().setPrettyPrinting().create()
         val instrumentationArgs =
-            if (isBenchmark && !isPostsubmit) {
+            if (isMicrobenchmark && !isPostsubmit) {
                 listOf(
                     InstrumentationArg("notAnnotation", "androidx.test.filters.FlakyTest"),
                     InstrumentationArg("androidx.benchmark.dryRunMode.enable", "true"),
@@ -91,11 +92,11 @@
         sb.append(MODULE_METADATA_TAG_OPTION.replace("APPLICATION_ID", applicationId))
             .append(WIFI_DISABLE_OPTION)
             .append(FLAKY_TEST_OPTION)
-        if (isBenchmark) {
+        if (isMicrobenchmark) {
             if (isPostsubmit) {
-                sb.append(BENCHMARK_POSTSUBMIT_OPTIONS)
+                sb.append(MICROBENCHMARK_POSTSUBMIT_OPTIONS)
             } else {
-                sb.append(BENCHMARK_PRESUBMIT_OPTION)
+                sb.append(MICROBENCHMARK_PRESUBMIT_OPTION)
             }
         }
         sb.append(SETUP_INCLUDE)
@@ -105,7 +106,7 @@
             sb.append(APK_INSTALL_OPTION.replace("APK_NAME", appApkName!!))
         sb.append(TARGET_PREPARER_CLOSE)
         // Post install commands after SuiteApkInstaller is declared
-        if (isBenchmark) {
+        if (isMicrobenchmark) {
             sb.append(benchmarkPostInstallCommandOption(applicationId))
         }
         sb.append(TEST_BLOCK_OPEN)
@@ -312,16 +313,15 @@
 """
         .trimIndent()
 
-private val BENCHMARK_PRESUBMIT_OPTION =
+private val MICROBENCHMARK_PRESUBMIT_OPTION =
     """
     <option name="instrumentation-arg" key="androidx.benchmark.dryRunMode.enable" value="true" />
 
 """
         .trimIndent()
 
-private val BENCHMARK_POSTSUBMIT_OPTIONS =
+private val MICROBENCHMARK_POSTSUBMIT_OPTIONS =
     """
-    <option name="instrumentation-arg" key="androidx.benchmark.output.enable" value="true" />
     <option name="instrumentation-arg" key="listener" value="androidx.benchmark.junit4.InstrumentationResultsRunListener" />
 
 """
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
index 87b7114..d428c0f 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
@@ -134,10 +134,14 @@
         configBuilder.isPostsubmit(!isPresubmit)
         // This section adds metadata tags that will help filter runners to specific modules.
         if (hasBenchmarkPlugin.get()) {
-            configBuilder.isBenchmark(true)
-            if (configBuilder.isPostsubmit) {
-                configBuilder.tag("microbenchmarks")
-            } else {
+            configBuilder.isMicrobenchmark(true)
+
+            // tag microbenchmarks as "microbenchmarks" in either build config, so that benchmark
+            // test configs will always have something to run, regardless of build (though presubmit
+            // builds will still set dry run, and not output metrics)
+            configBuilder.tag("microbenchmarks")
+
+            if (isPresubmit) {
                 // in presubmit, we treat micro benchmarks as regular correctness tests as
                 // they run with dryRunMode to check crashes don't happen, without measurement
                 configBuilder.tag("androidx_unit_tests")
diff --git a/buildSrc/repos.gradle b/buildSrc/repos.gradle
index 4ae93fa..9b5077d 100644
--- a/buildSrc/repos.gradle
+++ b/buildSrc/repos.gradle
@@ -65,13 +65,6 @@
                url("https://maven.pkg.jetbrains.space/public/p/compose/dev")
         }
         handler.mavenLocal()
-        // TODO(b/280646217): Remove after official release to gmaven.
-        handler.maven {
-            url("https://storage.googleapis.com/r8-releases/raw")
-            content {
-                includeModule("com.android.tools", "r8")
-            }
-        }
     }
     // Ordering appears to be important: b/229733266
     def androidPluginRepoOverride = System.getenv("GRADLE_PLUGIN_REPO")
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
index 273d30d..c8276d6 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
@@ -17,6 +17,7 @@
 package androidx.camera.camera2.pipe.integration.adapter
 
 import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
 import androidx.camera.camera2.pipe.CameraDevices
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.integration.internal.CameraGraphCreator
@@ -25,6 +26,7 @@
 import androidx.camera.core.CameraInfo
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.concurrent.CameraCoordinator
+import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
 import androidx.camera.core.concurrent.CameraCoordinator.CameraOperatingMode
 import androidx.camera.core.impl.CameraInternal
 
@@ -33,17 +35,15 @@
     cameraDevices: CameraDevices,
     private val cameraGraphCreator: CameraGraphCreator
 ) : CameraCoordinator {
-    private val cameraInternalMap = mutableMapOf<CameraId, CameraInternalAdapter>()
-
-    private var concurrentCameraIdsSet: Set<Set<CameraId>> = mutableSetOf()
-    private var concurrentCameraIdMap: MutableMap<String, MutableList<String>> = mutableMapOf()
-    private var activeConcurrentCameraInfosList: MutableList<CameraInfo> = mutableListOf()
-
-    private var concurrentMode: Int = CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
-    private var concurrentModeOn = false
+    @VisibleForTesting val cameraInternalMap = mutableMapOf<CameraId, CameraInternalAdapter>()
+    @VisibleForTesting var concurrentCameraIdsSet = mutableSetOf<Set<CameraId>>()
+    @VisibleForTesting var concurrentCameraIdMap = mutableMapOf<String, MutableList<String>>()
+    @VisibleForTesting var activeConcurrentCameraInfosList = mutableListOf<CameraInfo>()
+    @VisibleForTesting var concurrentMode: Int = CAMERA_OPERATING_MODE_UNSPECIFIED
+    @VisibleForTesting var concurrentModeOn = false
 
     init {
-        concurrentCameraIdsSet = cameraDevices.awaitConcurrentCameraIds()!!
+        concurrentCameraIdsSet = cameraDevices.awaitConcurrentCameraIds()!!.toMutableSet()
         for (cameraIdSet in concurrentCameraIdsSet) {
             val cameraIdsList = cameraIdSet.toList()
             if (cameraIdsList.size >= 2) {
@@ -129,4 +129,13 @@
 
     override fun removeListener(listener: CameraCoordinator.ConcurrentCameraModeListener) {
     }
+
+    override fun shutdown() {
+        cameraInternalMap.clear()
+        concurrentCameraIdsSet.clear()
+        concurrentCameraIdMap.clear()
+        activeConcurrentCameraInfosList.clear()
+        concurrentMode = CAMERA_OPERATING_MODE_UNSPECIFIED
+        concurrentModeOn = false
+    }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt
index 1222685..12c69f8 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt
@@ -25,6 +25,7 @@
 import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
 import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT
 import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_SINGLE
+import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
 import androidx.camera.core.impl.CameraInfoInternal
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -123,4 +124,22 @@
         assertThat(cameraCoordinatorAdapter.cameraOperatingMode)
             .isEqualTo(CAMERA_OPERATING_MODE_SINGLE)
     }
+
+    @Test
+    fun shutdown() {
+        cameraCoordinatorAdapter.cameraOperatingMode = CAMERA_OPERATING_MODE_CONCURRENT
+        cameraCoordinatorAdapter.activeConcurrentCameraInfos = mutableListOf(
+            FakeCameraInfoAdapterCreator.createCameraInfoAdapter(cameraId = CameraId("0")),
+            FakeCameraInfoAdapterCreator.createCameraInfoAdapter(cameraId = CameraId("1")))
+
+        cameraCoordinatorAdapter.shutdown()
+
+        assertThat(cameraCoordinatorAdapter.cameraInternalMap).isEmpty()
+        assertThat(cameraCoordinatorAdapter.activeConcurrentCameraInfos).isEmpty()
+        assertThat(cameraCoordinatorAdapter.concurrentCameraIdMap).isEmpty()
+        assertThat(cameraCoordinatorAdapter.concurrentCameraIdsSet).isEmpty()
+        assertThat(cameraCoordinatorAdapter.cameraOperatingMode).isEqualTo(
+            CAMERA_OPERATING_MODE_UNSPECIFIED)
+        assertThat(cameraCoordinatorAdapter.concurrentModeOn).isFalse()
+    }
 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinator.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinator.java
index 93e24c7..e6680bf 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinator.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinator.java
@@ -140,6 +140,15 @@
         mConcurrentCameraModeListeners.remove(listener);
     }
 
+    @Override
+    public void shutdown() {
+        mConcurrentCameraModeListeners.clear();
+        mConcurrentCameraIdMap.clear();
+        mActiveConcurrentCameraInfos.clear();
+        mConcurrentCameraIds.clear();
+        mCameraOperatingMode = CAMERA_OPERATING_MODE_UNSPECIFIED;
+    }
+
     private void retrieveConcurrentCameraIds() {
         try {
             mConcurrentCameraIds = mCameraManager.getConcurrentCameraIds();
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinatorTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinatorTest.kt
index 6b7de32..fa3f220 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinatorTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinatorTest.kt
@@ -57,8 +57,6 @@
 )
 class Camera2CameraCoordinatorTest {
 
-    private val mContext = ApplicationProvider.getApplicationContext<Context>()
-
     private lateinit var cameraCoordinator: CameraCoordinator
 
     @Before
@@ -109,26 +107,7 @@
 
     @Test
     fun getPairedCameraId() {
-        val characteristics0 = ShadowCameraCharacteristics.newCameraCharacteristics()
-        (Shadow.extract<Any>(
-            ApplicationProvider.getApplicationContext<Context>()
-                .getSystemService(Context.CAMERA_SERVICE)
-        ) as ShadowCameraManager)
-            .addCamera("0", characteristics0)
-        val characteristics1 = ShadowCameraCharacteristics.newCameraCharacteristics()
-        (Shadow.extract<Any>(
-            ApplicationProvider.getApplicationContext<Context>()
-                .getSystemService(Context.CAMERA_SERVICE)
-        ) as ShadowCameraManager)
-            .addCamera("1", characteristics1)
-
-        val mCameraManagerCompat =
-            CameraManagerCompat.from((ApplicationProvider.getApplicationContext() as Context))
-
-        cameraCoordinator.activeConcurrentCameraInfos = listOf(
-            Camera2CameraInfoImpl("0", mCameraManagerCompat),
-            Camera2CameraInfoImpl("1", mCameraManagerCompat)
-        )
+        cameraCoordinator.activeConcurrentCameraInfos = createConcurrentCameraInfos()
 
         assertThat(cameraCoordinator.getPairedConcurrentCameraId("0")).isEqualTo("1")
         assertThat(cameraCoordinator.getPairedConcurrentCameraId("1")).isEqualTo("0")
@@ -164,6 +143,41 @@
             anyInt(), anyInt())
     }
 
+    @Test
+    fun shutdown() {
+        cameraCoordinator.cameraOperatingMode = CAMERA_OPERATING_MODE_CONCURRENT
+        cameraCoordinator.activeConcurrentCameraInfos = createConcurrentCameraInfos()
+
+        cameraCoordinator.shutdown()
+
+        assertThat(cameraCoordinator.concurrentCameraSelectors).isEmpty()
+        assertThat(cameraCoordinator.activeConcurrentCameraInfos).isEmpty()
+        assertThat(cameraCoordinator.cameraOperatingMode).isEqualTo(
+            CAMERA_OPERATING_MODE_UNSPECIFIED)
+    }
+
+    private fun createConcurrentCameraInfos(): List<Camera2CameraInfoImpl> {
+        val characteristics0 = ShadowCameraCharacteristics.newCameraCharacteristics()
+        (Shadow.extract<Any>(
+            ApplicationProvider.getApplicationContext<Context>()
+                .getSystemService(Context.CAMERA_SERVICE)
+        ) as ShadowCameraManager)
+            .addCamera("0", characteristics0)
+        val characteristics1 = ShadowCameraCharacteristics.newCameraCharacteristics()
+        (Shadow.extract<Any>(
+            ApplicationProvider.getApplicationContext<Context>()
+                .getSystemService(Context.CAMERA_SERVICE)
+        ) as ShadowCameraManager)
+            .addCamera("1", characteristics1)
+        val cameraManagerCompat =
+            CameraManagerCompat.from((ApplicationProvider.getApplicationContext() as Context))
+
+        return listOf(
+            Camera2CameraInfoImpl("0", cameraManagerCompat),
+            Camera2CameraInfoImpl("1", cameraManagerCompat)
+        )
+    }
+
     private class FakeCameraManagerImpl : CameraManagerCompat.CameraManagerCompatImpl {
 
         private val mCameraManagerImpl = CameraManagerCompat.CameraManagerCompatImpl.from(
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/concurrent/CameraCoordinator.java b/camera/camera-core/src/main/java/androidx/camera/core/concurrent/CameraCoordinator.java
index 60d6be3..b98d11e 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/concurrent/CameraCoordinator.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/concurrent/CameraCoordinator.java
@@ -130,6 +130,11 @@
     void removeListener(@NonNull ConcurrentCameraModeListener listener);
 
     /**
+     * Clean up all the resources when CameraX shutdown.
+     */
+    void shutdown();
+
+    /**
      * Interface for concurrent camera mode update.
      *
      * <p>Everytime user changes {@link CameraOperatingMode}, the observer will be notified and
diff --git a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
index f585001..7dbc5c3 100644
--- a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
+++ b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
@@ -30,6 +30,7 @@
 import androidx.camera.core.ConcurrentCamera.SingleCameraConfig
 import androidx.camera.core.Preview
 import androidx.camera.core.UseCaseGroup
+import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
 import androidx.camera.core.impl.CameraFactory
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
 import androidx.camera.testing.fakes.FakeAppConfig
@@ -63,6 +64,7 @@
     private val context = ApplicationProvider.getApplicationContext() as Context
     private val lifecycleOwner0 = FakeLifecycleOwner()
     private val lifecycleOwner1 = FakeLifecycleOwner()
+    private val cameraCoordinator = FakeCameraCoordinator()
 
     private lateinit var provider: ProcessCameraProvider
 
@@ -665,6 +667,10 @@
 
         // Should not throw exception
         ProcessCameraProvider.configureInstance(FakeAppConfig.create())
+        assertThat(cameraCoordinator.cameraOperatingMode).isEqualTo(
+            CAMERA_OPERATING_MODE_UNSPECIFIED)
+        assertThat(cameraCoordinator.concurrentCameraSelectors).isEmpty()
+        assertThat(cameraCoordinator.activeConcurrentCameraInfos).isEmpty()
     }
 
     @Test
@@ -841,7 +847,6 @@
     }
 
     private fun createConcurrentCameraAppConfig(): CameraXConfig {
-        val cameraCoordinator = FakeCameraCoordinator()
         val combination0 = mapOf(
             "0" to CameraSelector.Builder().requireLensFacing(LENS_FACING_BACK).build(),
             "1" to CameraSelector.Builder().requireLensFacing(LENS_FACING_FRONT).build())
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
index d675fb8..f6e56cd 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
@@ -291,6 +291,10 @@
             mLifecycleCameraRepository.clear();
         });
 
+        if (mCameraX != null) {
+            mCameraX.getCameraFactory().getCameraCoordinator().shutdown();
+        }
+
         ListenableFuture<Void> shutdownFuture = mCameraX != null ? mCameraX.shutdown() :
                 Futures.immediateFuture(null);
 
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraCoordinator.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraCoordinator.java
index 3281ec1..1dc5b00 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraCoordinator.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraCoordinator.java
@@ -125,4 +125,13 @@
     public void removeListener(@NonNull ConcurrentCameraModeListener listener) {
         mConcurrentCameraModeListeners.remove(listener);
     }
+
+    @Override
+    public void shutdown() {
+        mConcurrentCameraIdMap.clear();
+        mConcurrentCameraIds.clear();
+        mConcurrentCameraSelectors.clear();
+        mActiveConcurrentCameraInfos.clear();
+        mConcurrentCameraModeListeners.clear();
+    }
 }
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 5c2d5cb..67d58c8e 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -900,6 +900,7 @@
   }
 
   @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class ConversationItem implements androidx.car.app.model.Item {
+    method public java.util.List<androidx.car.app.model.Action!> getActions();
     method public androidx.car.app.messaging.model.ConversationCallbackDelegate getConversationCallbackDelegate();
     method public androidx.car.app.model.CarIcon? getIcon();
     method public String getId();
@@ -912,6 +913,7 @@
   public static final class ConversationItem.Builder {
     ctor public ConversationItem.Builder();
     ctor public ConversationItem.Builder(androidx.car.app.messaging.model.ConversationItem);
+    method public androidx.car.app.messaging.model.ConversationItem.Builder addAction(androidx.car.app.model.Action);
     method public androidx.car.app.messaging.model.ConversationItem build();
     method public androidx.car.app.messaging.model.ConversationItem.Builder setConversationCallback(androidx.car.app.messaging.model.ConversationCallback);
     method public androidx.car.app.messaging.model.ConversationItem.Builder setGroupConversation(boolean);
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 5c2d5cb..67d58c8e 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -900,6 +900,7 @@
   }
 
   @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class ConversationItem implements androidx.car.app.model.Item {
+    method public java.util.List<androidx.car.app.model.Action!> getActions();
     method public androidx.car.app.messaging.model.ConversationCallbackDelegate getConversationCallbackDelegate();
     method public androidx.car.app.model.CarIcon? getIcon();
     method public String getId();
@@ -912,6 +913,7 @@
   public static final class ConversationItem.Builder {
     ctor public ConversationItem.Builder();
     ctor public ConversationItem.Builder(androidx.car.app.messaging.model.ConversationItem);
+    method public androidx.car.app.messaging.model.ConversationItem.Builder addAction(androidx.car.app.model.Action);
     method public androidx.car.app.messaging.model.ConversationItem build();
     method public androidx.car.app.messaging.model.ConversationItem.Builder setConversationCallback(androidx.car.app.messaging.model.ConversationCallback);
     method public androidx.car.app.messaging.model.ConversationItem.Builder setGroupConversation(boolean);
diff --git a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
index d83d5b0..1acc0f4 100644
--- a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
+++ b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
@@ -29,13 +29,16 @@
 import androidx.car.app.annotations.ExperimentalCarApi;
 import androidx.car.app.annotations.KeepFields;
 import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.model.Action;
 import androidx.car.app.model.CarIcon;
 import androidx.car.app.model.CarText;
 import androidx.car.app.model.Item;
+import androidx.car.app.model.constraints.ActionsConstraints;
 import androidx.car.app.utils.CollectionUtils;
 import androidx.core.app.Person;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -58,6 +61,8 @@
     private final List<CarMessage> mMessages;
     @NonNull
     private final ConversationCallbackDelegate mConversationCallbackDelegate;
+    @NonNull
+    private final List<Action> mActions;
 
     @Override
     public int hashCode() {
@@ -67,7 +72,8 @@
                 mTitle,
                 mIcon,
                 mIsGroupConversation,
-                mMessages
+                mMessages,
+                mActions
         );
     }
 
@@ -89,6 +95,7 @@
                         .arePersonsEqual(getSelf(), otherConversationItem.getSelf())
                         && mIsGroupConversation == otherConversationItem.mIsGroupConversation
                         && Objects.equals(mMessages, otherConversationItem.mMessages)
+                        && Objects.equals(mActions, otherConversationItem.mActions)
                 ;
     }
 
@@ -101,6 +108,7 @@
         this.mMessages = requireNonNull(CollectionUtils.unmodifiableCopy(builder.mMessages));
         checkState(!mMessages.isEmpty(), "Message list cannot be empty.");
         this.mConversationCallbackDelegate = requireNonNull(builder.mConversationCallbackDelegate);
+        this.mActions = CollectionUtils.unmodifiableCopy(builder.mActions);
     }
 
     /** Default constructor for serialization. */
@@ -123,6 +131,7 @@
                         // Do nothing
                     }
                 });
+        mActions = Collections.emptyList();
     }
 
     /**
@@ -175,6 +184,16 @@
     }
 
     /**
+     * Returns the list of additional actions.
+     *
+     * @see ConversationItem.Builder#addAction(Action)
+     */
+    @NonNull
+    public List<Action> getActions() {
+        return mActions;
+    }
+
+    /**
      * Verifies that a given {@link Person} has the required fields to be a message sender. Returns
      * the input {@link Person} if valid, or throws an exception if invalid.
      *
@@ -202,6 +221,7 @@
         List<CarMessage> mMessages;
         @Nullable
         ConversationCallbackDelegate mConversationCallbackDelegate;
+        final List<Action> mActions;
 
         /**
          * Specifies a unique identifier for the conversation
@@ -278,6 +298,23 @@
             return this;
         }
 
+        /**
+         * Adds an additional action for the conversation.
+         *
+         * @throws NullPointerException     if {@code action} is {@code null}
+         * @throws IllegalArgumentException if {@code action} contains unsupported Action types,
+         *                                  exceeds the maximum number of allowed actions (1) or
+         *                                  does not contain a valid {@link CarIcon}.
+         */
+        @NonNull
+        public Builder addAction(@NonNull Action action) {
+            List<Action> mActionsCopy = new ArrayList<>(mActions);
+            mActionsCopy.add(requireNonNull(action));
+            ActionsConstraints.ACTIONS_CONSTRAINTS_CONVERSATION_ITEM.validateOrThrow(mActionsCopy);
+            mActions.add(action);
+            return this;
+        }
+
         /** Returns a new {@link ConversationItem} instance defined by this builder */
         @NonNull
         public ConversationItem build() {
@@ -286,6 +323,7 @@
 
         /** Returns an empty {@link Builder} instance. */
         public Builder() {
+            mActions = new ArrayList<>();
         }
 
         /** Returns a builder from the given {@link ConversationItem}. */
@@ -297,6 +335,7 @@
             this.mIsGroupConversation = other.isGroupConversation();
             this.mConversationCallbackDelegate = other.getConversationCallbackDelegate();
             this.mMessages = other.getMessages();
+            this.mActions = new ArrayList<>(other.getActions());
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
index b91efc0f..e61e54b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
@@ -145,6 +145,19 @@
                     .build();
 
     /**
+     * Constraints for additional ConversationItem actions. Only allows custom actions.
+     */
+    @NonNull
+    public static final ActionsConstraints ACTIONS_CONSTRAINTS_CONVERSATION_ITEM =
+            new ActionsConstraints.Builder()
+                    .setMaxActions(1)
+                    .setMaxCustomTitles(1)
+                    .addAllowedActionType(Action.TYPE_CUSTOM)
+                    .setRequireActionIcons(true)
+                    .setOnClickListenerAllowed(true)
+                    .build();
+
+    /**
      * Constraints for floating action buttons.
      *
      * <p>Only buttons with icons and background color are allowed.
diff --git a/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java b/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java
index ef2956c..e6b6e49 100644
--- a/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java
@@ -21,8 +21,11 @@
 import static org.junit.Assert.assertThrows;
 
 import androidx.annotation.NonNull;
+import androidx.car.app.TestUtils;
+import androidx.car.app.model.Action;
 import androidx.car.app.model.CarIcon;
 import androidx.car.app.model.CarText;
+import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -191,6 +194,77 @@
         assertEqual(fullyPopulatedItem, modifiedConversationCallback);
     }
 
+    @Test
+    public void addAction() {
+        CarIcon icon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                "ic_test_1");
+        Action customAction = new Action.Builder().setIcon(icon).build();
+        ConversationItem item =
+                TestConversationFactory
+                        .createFullyPopulatedConversationItemBuilder()
+                        .addAction(customAction)
+                        .build();
+
+        assertThat(item.getActions()).containsExactly(customAction);
+    }
+
+    @Test
+    public void addAction_appIconInvalid_throws() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> TestConversationFactory
+                        .createFullyPopulatedConversationItemBuilder()
+                        .addAction(Action.APP_ICON)
+                        .build());
+    }
+
+    @Test
+    public void addAction_backInvalid_throws() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> TestConversationFactory
+                        .createFullyPopulatedConversationItemBuilder()
+                        .addAction(Action.BACK)
+                        .build());
+    }
+
+    @Test
+    public void addAction_panInvalid_throws() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> TestConversationFactory
+                        .createFullyPopulatedConversationItemBuilder()
+                        .addAction(Action.PAN)
+                        .build());
+    }
+
+    @Test
+    public void addAction_manyActions_throws() {
+        CarIcon icon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                "ic_test_1");
+        Action customAction = new Action.Builder().setIcon(icon).build();
+
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> TestConversationFactory
+                        .createFullyPopulatedConversationItemBuilder()
+                        .addAction(customAction)
+                        .addAction(customAction)
+                        .build());
+    }
+
+    @Test
+    public void addAction_invalidActionNullIcon_throws() {
+        Action customAction = TestUtils.createAction("Title", /* icon= */ null);
+
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> TestConversationFactory
+                        .createFullyPopulatedConversationItemBuilder()
+                        .addAction(customAction)
+                        .build());
+    }
+
     private void assertEqual(ConversationItem item1, ConversationItem item2) {
         assertThat(item1).isEqualTo(item2);
         assertThat(item1.hashCode()).isEqualTo(item2.hashCode());
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithBoxWithConstraints.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithBoxWithConstraints.kt
index 148eb8b..bfcd637 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithBoxWithConstraints.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithBoxWithConstraints.kt
@@ -18,6 +18,7 @@
 
 package androidx.compose.animation.demos.lookahead
 
+import android.annotation.SuppressLint
 import androidx.compose.animation.demos.gesture.pastelColors
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
@@ -49,6 +50,7 @@
 import androidx.compose.ui.layout.LookaheadScope
 import androidx.compose.ui.unit.dp
 
+@SuppressLint("UnusedBoxWithConstraintsScope")
 @Composable
 fun LookaheadWithBoxWithConstraints() {
     Box(Modifier.fillMaxSize()) {
diff --git a/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/BoxWithConstraintsDetector.kt b/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/BoxWithConstraintsDetector.kt
new file mode 100644
index 0000000..9667018
--- /dev/null
+++ b/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/BoxWithConstraintsDetector.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2023 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.foundation.lint
+
+import androidx.compose.lint.isInPackageName
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.android.tools.lint.detector.api.computeKotlinArgumentMapping
+import com.intellij.psi.PsiMethod
+import java.util.EnumSet
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.ULambdaExpression
+import org.jetbrains.uast.USimpleNameReferenceExpression
+import org.jetbrains.uast.tryResolve
+import org.jetbrains.uast.visitor.AbstractUastVisitor
+
+class BoxWithConstraintsDetector : Detector(), SourceCodeScanner {
+    override fun getApplicableMethodNames(): List<String> = listOf(
+        FoundationNames.Layout.BoxWithConstraints.shortName
+    )
+
+    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+        if (method.isInPackageName(FoundationNames.Layout.PackageName)) {
+            val contentArgument = computeKotlinArgumentMapping(node, method)
+                .orEmpty()
+                .filter { (_, parameter) ->
+                    parameter.name == "content"
+                }
+                .keys
+                .filterIsInstance<ULambdaExpression>()
+                .firstOrNull() ?: return
+
+            var foundValidReference = false
+            contentArgument.accept(object : AbstractUastVisitor() {
+                override fun visitSimpleNameReferenceExpression(
+                    node: USimpleNameReferenceExpression
+                ): Boolean {
+                    val reference = (node.tryResolve() as? PsiMethod)
+                        ?: return foundValidReference // No need to continue if we have already found one
+                    if (reference.isInPackageName(FoundationNames.Layout.PackageName) &&
+                        reference.containingClass?.name == "BoxWithConstraintsScope"
+                    ) {
+                        foundValidReference = true
+                    }
+                    return foundValidReference
+                }
+            })
+            if (!foundValidReference) {
+                context.report(
+                    UnusedConstraintsParameter,
+                    node,
+                    context.getLocation(contentArgument),
+                    "BoxWithConstraints scope is not used"
+                )
+            }
+        }
+    }
+
+    companion object {
+        val UnusedConstraintsParameter = Issue.create(
+            "UnusedBoxWithConstraintsScope",
+            "BoxWithConstraints content should use the constraints provided " +
+                "as via BoxWithConstraintsScope",
+            "The `content` lambda in BoxWithConstraints has a scope " +
+                "which will include the incoming constraints. If this " +
+                "scope is ignored, then the cost of subcomposition is being wasted and " +
+                "this BoxWithConstraints should be replaced with a Box.",
+            Category.CORRECTNESS, 3, Severity.ERROR,
+            Implementation(
+                BoxWithConstraintsDetector::class.java,
+                EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+            )
+        )
+    }
+}
diff --git a/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationIssueRegistry.kt b/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationIssueRegistry.kt
index 2a936c1..d3de50b 100644
--- a/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationIssueRegistry.kt
+++ b/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationIssueRegistry.kt
@@ -32,7 +32,8 @@
     override val issues get() = listOf(
         LazyLayoutStateReadInCompositionDetector.FrequentlyChangedStateReadInComposition,
         UnrememberedMutableInteractionSourceDetector.UnrememberedMutableInteractionSource,
-        NonLambdaOffsetModifierDetector.UseOfNonLambdaOverload
+        NonLambdaOffsetModifierDetector.UseOfNonLambdaOverload,
+        BoxWithConstraintsDetector.UnusedConstraintsParameter
     )
     override val vendor = Vendor(
         vendorName = "Jetpack Compose",
diff --git a/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationNames.kt b/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationNames.kt
index b6a07ab..99d313a 100644
--- a/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationNames.kt
+++ b/compose/foundation/foundation-lint/src/main/java/androidx/compose/foundation/lint/FoundationNames.kt
@@ -41,5 +41,6 @@
         val PackageName = Package(FoundationNames.PackageName, "layout")
         val Offset = Name(PackageName, "offset")
         val AbsoluteOffset = Name(PackageName, "absoluteOffset")
+        val BoxWithConstraints = Name(PackageName, "BoxWithConstraints")
     }
 }
diff --git a/compose/foundation/foundation-lint/src/test/java/androidx/compose/foundation/lint/BoxWithConstraintsDetectorTest.kt b/compose/foundation/foundation-lint/src/test/java/androidx/compose/foundation/lint/BoxWithConstraintsDetectorTest.kt
new file mode 100644
index 0000000..947f2b6
--- /dev/null
+++ b/compose/foundation/foundation-lint/src/test/java/androidx/compose/foundation/lint/BoxWithConstraintsDetectorTest.kt
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2023 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.foundation.lint
+
+import androidx.compose.lint.test.Stubs
+import androidx.compose.lint.test.bytecodeStub
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BoxWithConstraintsDetectorTest : LintDetectorTest() {
+    override fun getDetector(): Detector = BoxWithConstraintsDetector()
+
+    override fun getIssues(): MutableList<Issue> =
+        mutableListOf(BoxWithConstraintsDetector.UnusedConstraintsParameter)
+
+    private val BoxWithConstraintsStub = bytecodeStub(
+        filename = "BoxWithConstraints.kt",
+        filepath = "androidx/compose/foundation/layout",
+        checksum = 0xddc1f733,
+        source =
+        """
+            package androidx.compose.foundation.layout
+
+            import androidx.compose.runtime.Composable
+
+            interface Constraints {
+                val minWidth: Int
+            }
+            interface Dp {}
+            interface BoxWithConstraintsScope {
+                val constraints: Constraints
+                val minWidth: Dp
+                val maxWidth: Dp
+                val minHeight: Dp
+                val maxHeight: Dp
+            }
+
+            @Composable
+            fun BoxWithConstraints(
+                propagateMinConstraints: Boolean = false,
+                content: @Composable BoxWithConstraintsScope.() -> Unit
+            ) {}
+        """.trimIndent(),
+        """
+                META-INF/main.kotlin_module:
+                H4sIAAAAAAAA/2NgYGBmYGBgBGJ2KM3AZcWllJiXUpSfmVKhl5yfW5BfnKqX
+                ll+al5JYkpmfp5eTWJlfWiIk4pRfEZ5ZkuGcn1dcUpSYmVdS7F3CxcvFnJaf
+                L8QWklpc4l2ixKDFAAD5174zYwAAAA==
+                """,
+        """
+                androidx/compose/foundation/layout/BoxWithConstraintsKt.class:
+                H4sIAAAAAAAA/6VTXU8TQRQ9sy394qsU+SqIKCAgwhZiwkOJiRKJjQWNRYzw
+                NGyHsrSdaXZnCbwY/oav/gPfiA+G+OiPMt7ZtlrsA4lusnfunTl77p17z/74
+                +fUbgCdYZ9jgsuwpt3xuO6reUL6wj1Ugy1y7Sto1fqECbT9X5+9dfbKlpK89
+                7krtv9JxMIb0KT/jhJIV+/XRqXBoN8KQ6cYzzC0eFKtK11xpn57V7eNAOiaF
+                b2+3vLX80j5D41bY5krxn0ouOaoh8m3yd9LV+adhyvluPi+Q2q0LeyuM+VFN
+                5Blmi8qr2KdCHxlC3+ZSKs2b1e0qvRvUaoSKO0pqIXUCKYbpjqtQDcKTvGYX
+                pPboe9fx4+hjGHFOhFNtEbzhHq8LAjIsLBb/7m6+Y6dkSCp0gT4MYDCFfqQZ
+                xhqeavAK12LHlTfazw4YZm4bAEO2u29zZXHMg5omqdw+wkJ3zabCHsRSsDDG
+                MNRm2BGa08g4JbXqZxGSIzMmTqVWjWPR/rlrvBx55TWGD9eX06nry5SVtppL
+                b7iMW51vNpe+vsxaObaeSBCQvMj6VDqaHc9EM1YuFlqW6/n+OWYl4qFNvIyb
+                BPQrgKTbLq+zKZv/IzgSQZvzxTkJw6dv2uR7FyFgpPvb1Sr1O7qlyoJhsOhK
+                sRvUj4S3Z6RoqlQOr+1zzzVxazNZciuS68Ajf/JtU8AFeeb6Lh0/+6NV+hX/
+                Pv2tuhuw/pLmTnWHN1oJUiUVeI7Ydk0w0eLY7+LHGk06CvNYmDCjp2iJojzF
+                lhnxcqb3CkNfQsAjsjFqfAwjWCZ/tAlBBsMhRRwp3KHzxyE6jpUWPkHrKr1J
+                A6fLA+kkUYySb3JttGoYmIp+/ISeSD67fIXxZkqbbAQsEeYegJFYhjiHiTND
+                x7kQtEjXALaJzlwhe4hIAZMFTJHF3QKmca+AGdw/BPPxALOHSPro8THnIxPa
+                lI95Hw99JHws/AJecgBzcAUAAA==
+                """,
+        """
+                androidx/compose/foundation/layout/BoxWithConstraintsScope.class:
+                H4sIAAAAAAAA/5WSzW7aQBDH/2vA2IYQJ21aQvqdSm0uNUU9tb30Q1WRSCol
+                UhOJkzEOLNi7iF0QvfEUfYAe+hA9VCjHPlTVMSUBQSpRazWz89N/dtYz++v3
+                j58AXuAxw0tfNPuSN0deIOOeVKF3Lgei6WsuhRf5X+RAe2/l6JTr9jsplO77
+                XGh1EshemAVjcDv+0CehaHmfGp0w0FmkGAqtUC/IGSpPD2prVFrIecWwX5P9
+                ltcJdSNByvOFkHqqV96R1EeDKCJVjmodcnHKm7rNcLBeofe9y0x/NMvM/z3n
+                Y8hbbT0L/dFluFXrSh1x4R2G2qezfMo34mGK+sgSk2VgXUIjnkRl2jWfM3yd
+                jEuOUTQcw52MHVrTvZWaecs6L07GFaPMjrddo2SUU2cX39MX30yzlLbSboao
+                STS7QC3XJuos0dyU5pfoxpQWluimaye3qzC8XqdT/xg+/T6og8HikMv/P2I7
+                nrf4ydqDs+KrqdnxfGRWfPUMdlav/axLkr3jgdA8DqtiyBVvROGb+ZticE7k
+                oB+EH3gUMuzOpJ9XhCb1D2kkXybNkIFJrXhIUeKzADEL9gpzrmG5a1h+mVG1
+                R1P7APvk60Q3qGqhjlQVm1W4ZLGVmO0qbuAmCRR2cKsOV+G2QlFhV6GkkFEw
+                FfYU7ijkFWyFuwqOwj2FnMJ9BesPb1xrKxsEAAA=
+                """,
+        """
+                androidx/compose/foundation/layout/Constraints.class:
+                H4sIAAAAAAAA/5WPz07CQBDGv9mWUot/CooiT6AXWonxYjyoiQkJxAQTMeFU
+                aIEVumvYheCNZ/HgQ3gwhKMPZdxyMF7dbH47szO73zdf3x+fAM5xRKhFIp5K
+                Hi+CvkxfpEqCgZyJONJcimASvcqZDm6lUHoacaFVHkTwn6N5ZIpiGNz3npO+
+                zsMiFIaJbnHR4bEeEayT0wah2BxLPeEiaCU6Mp9GlwSWzi2jThnyBBqbqwXP
+                stBE8RnhYrUseazCPOavlp7ZzHc95jJ3UFkt6yykdslnVRZaT+t3e/3mOFXb
+                tf1c9rpOCJv/G8lYAsFNf62Xb+Siw/XoT09trAneg5xN+8kdnySE4/ZMaJ4m
+                j1zx3iS5FkLqjYJyjA/YyBbZhBwcEzGUNzzAoTmvjGDeVNwurAa2GvAMUciw
+                3cAOdrsghT34XTgKRYWSwv6GOQXnB5/tt/C/AQAA
+                """,
+        """
+                androidx/compose/foundation/layout/Dp.class:
+                H4sIAAAAAAAA/41Oy07DQAwcb6Ep4ZXykMoHIG6krXrjxENIlYqQQIJDT9tm
+                C9sku1V2U4Vbv4sD6pmPQjjlB7Cl8diWZ/z98/kFYIATwrk0SWF1UsVTmy+s
+                U/HMliaRXlsTZ/LDlj6+WwQgQjSXS8kz8xY/TuZq6gM0CO1Ran2mTfygvOQ7
+                eUUQ+bLBBlRDQKCUR5Wuuy6zpEc4Xa9aoeiIUETMZp31qi+6VC/7hIvRv55i
+                I7DSja1etX+/tcb5Qmrj3WXqCeGzLYuputeZIpw9lcbrXL1opyeZujbG+o2a
+                a7IntvAXAkcbbOOYa4/VtzmbYzSGCIZoMWKnhnCIXeyNQQ77OBhDOBw6RL/m
+                nxtjWQEAAA==
+                """
+    )
+
+    @Test
+    fun unreferencedConstraints() {
+        lint().files(
+            kotlin(
+                """
+                package foo
+
+                import androidx.compose.foundation.layout.BoxWithConstraints
+                import androidx.compose.runtime.Composable
+
+                @Composable
+                fun Test() {
+                    val foo = 123
+                    BoxWithConstraints { /**/ }
+                    BoxWithConstraints { foo }
+                    BoxWithConstraints(content = { /**/ })
+                    BoxWithConstraints(propagateMinConstraints = false, content = { /**/ })
+                }
+                """.trimIndent()
+            ),
+            BoxWithConstraintsStub,
+            Stubs.Composable,
+        )
+            .run()
+            .expect(
+                """
+src/foo/test.kt:9: Error: BoxWithConstraints scope is not used [UnusedBoxWithConstraintsScope]
+    BoxWithConstraints { /**/ }
+                       ~~~~~~~~
+src/foo/test.kt:10: Error: BoxWithConstraints scope is not used [UnusedBoxWithConstraintsScope]
+    BoxWithConstraints { foo }
+                       ~~~~~~~
+src/foo/test.kt:11: Error: BoxWithConstraints scope is not used [UnusedBoxWithConstraintsScope]
+    BoxWithConstraints(content = { /**/ })
+                                 ~~~~~~~~
+src/foo/test.kt:12: Error: BoxWithConstraints scope is not used [UnusedBoxWithConstraintsScope]
+    BoxWithConstraints(propagateMinConstraints = false, content = { /**/ })
+                                                                  ~~~~~~~~
+4 errors, 0 warnings
+                """
+            )
+    }
+
+    @Test
+    fun referencedConstraints() {
+        lint().files(
+            kotlin(
+                """
+                package foo
+
+                import androidx.compose.foundation.layout.BoxWithConstraints
+                import androidx.compose.runtime.Composable
+
+                @Composable
+                fun Foo(content: @Composable ()->Unit) {}
+                @Composable
+                fun Bar() {}
+
+                @Composable
+                fun Test() {
+                    BoxWithConstraints { constraints }
+                    BoxWithConstraints { constraints.minWidth }
+                    BoxWithConstraints { minWidth }
+                    BoxWithConstraints { maxWidth }
+                    BoxWithConstraints { minHeight }
+                    BoxWithConstraints { maxHeight }
+                    BoxWithConstraints(content = { maxWidth })
+                    BoxWithConstraints(propagateMinConstraints = false, content = { minWidth })
+                    BoxWithConstraints {
+                        if (constraints.minWidth > 100) {
+                            Foo {}
+                        } else {
+                            Bar()
+                        }
+                    }
+                    BoxWithConstraints {
+                        Foo {
+                            constraints
+                        }
+                    }
+                }
+                """.trimIndent()
+            ),
+            BoxWithConstraintsStub,
+            Stubs.Composable,
+        )
+            .run()
+            .expectClean()
+    }
+}
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/LazyBoxWithConstraintsActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/LazyBoxWithConstraintsActivity.kt
index d4714d9..54c3d1a 100644
--- a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/LazyBoxWithConstraintsActivity.kt
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/LazyBoxWithConstraintsActivity.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.integration.macrobenchmark.target
 
+import android.annotation.SuppressLint
 import android.os.Bundle
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
@@ -73,6 +74,7 @@
     }
 }
 
+@SuppressLint("UnusedBoxWithConstraintsScope") // Need the nested subcompose layout for testing
 @Composable
 private fun NonLazyRow(entry: NestedListEntry) {
     BoxWithConstraints {
diff --git a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java
index 203ba8b..e66d965 100644
--- a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java
+++ b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java
@@ -40,6 +40,7 @@
 
 import org.hamcrest.Matcher;
 import org.junit.After;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import java.util.concurrent.CountDownLatch;
@@ -132,6 +133,7 @@
         verifyBarViewStacking(textView, 0);
     }
 
+    @Ignore // b/292019798
     @Test
     public void testBehaviorBasedSlidingFromClassAnnotation() {
         // Use a layout in which a custom child view has Behavior object configured via
@@ -160,6 +162,7 @@
         verifyBarViewStacking(textView, 0);
     }
 
+    @Ignore // b/292021877
     @Test
     public void testBehaviorBasedSlidingFromRuntimeApiCall() {
         // Use a layout in which a TextView child doesn't have any configured Behavior
diff --git a/core/core-performance-play-services/build.gradle b/core/core-performance-play-services/build.gradle
index f6643d6..42cab7be 100644
--- a/core/core-performance-play-services/build.gradle
+++ b/core/core-performance-play-services/build.gradle
@@ -25,7 +25,12 @@
 dependencies {
     api(libs.kotlinStdlib)
 
+    implementation(libs.playServicesDevicePerformance)
+
+    // Coroutines
     implementation(libs.kotlinCoroutinesCore)
+    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.1.1")
+
     implementation(project(":core:core-performance"))
 
     testImplementation(libs.testCore)
@@ -37,6 +42,9 @@
 }
 
 android {
+    defaultConfig {
+        minSdkVersion 24
+    }
     namespace "androidx.core.performance.play.services"
 }
 
diff --git a/core/core-performance-play-services/src/main/java/androidx/core/performance/play/services/PlayServicesDevicePerformanceRetriever.kt b/core/core-performance-play-services/src/main/java/androidx/core/performance/play/services/PlayServicesDevicePerformanceRetriever.kt
index 0682f5a..ea1ea30 100644
--- a/core/core-performance-play-services/src/main/java/androidx/core/performance/play/services/PlayServicesDevicePerformanceRetriever.kt
+++ b/core/core-performance-play-services/src/main/java/androidx/core/performance/play/services/PlayServicesDevicePerformanceRetriever.kt
@@ -18,6 +18,9 @@
 
 import android.content.Context
 import androidx.core.performance.DevicePerformanceRetriever
+import com.google.android.gms.deviceperformance.DevicePerformanceClient
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.tasks.await
 
 /**
  * A DevicePerformanceRetriever that uses Google Play Services to retrieve media performance class data.
@@ -31,10 +34,11 @@
             );
 
         companion object {
-            @Suppress("UNUSED_PARAMETER")
             @JvmStatic
             fun getPerformanceClass(context: Context): Int {
-                return 0
+                val client: DevicePerformanceClient =
+                    com.google.android.gms.deviceperformance.DevicePerformance.getClient(context)
+                return runBlocking { client.mediaPerformanceClass().await() }
             }
         }
 }
diff --git a/core/core-performance-testing/build.gradle b/core/core-performance-testing/build.gradle
index 2109a27..39b4dd1 100644
--- a/core/core-performance-testing/build.gradle
+++ b/core/core-performance-testing/build.gradle
@@ -36,6 +36,9 @@
 }
 
 android {
+    defaultConfig {
+        minSdkVersion 24
+    }
     namespace "androidx.core.performance.testing"
 }
 
diff --git a/core/core-performance/build.gradle b/core/core-performance/build.gradle
index 044a5ad..3dd2a66 100644
--- a/core/core-performance/build.gradle
+++ b/core/core-performance/build.gradle
@@ -47,5 +47,8 @@
 }
 
 android {
+    defaultConfig {
+        minSdkVersion 24
+    }
     namespace "androidx.core.performance"
 }
diff --git a/core/core-performance/samples/build.gradle b/core/core-performance/samples/build.gradle
index cbd177a..0087990 100644
--- a/core/core-performance/samples/build.gradle
+++ b/core/core-performance/samples/build.gradle
@@ -38,5 +38,8 @@
 }
 
 android {
+    defaultConfig {
+        minSdkVersion 24
+    }
     namespace "androidx.core.performance.samples"
 }
diff --git a/core/core/api/1.12.0-beta01.txt b/core/core/api/1.12.0-beta01.txt
index 4bb3fb0..7ff5c2a 100644
--- a/core/core/api/1.12.0-beta01.txt
+++ b/core/core/api/1.12.0-beta01.txt
@@ -2390,6 +2390,7 @@
   public class TypedValueCompat {
     method public static float deriveDimension(int, float, android.util.DisplayMetrics);
     method public static float dpToPx(float, android.util.DisplayMetrics);
+    method public static int getUnitFromComplexDimension(int);
     method public static float pxToDp(float, android.util.DisplayMetrics);
     method public static float pxToSp(float, android.util.DisplayMetrics);
     method public static float spToPx(float, android.util.DisplayMetrics);
@@ -4199,6 +4200,7 @@
     method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
     method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
     method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLineHeight(android.widget.TextView, int, @FloatRange(from=0) float);
     method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
     method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
     method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
diff --git a/core/core/api/current.ignore b/core/core/api/current.ignore
index f623e8e..a3517f0 100644
--- a/core/core/api/current.ignore
+++ b/core/core/api/current.ignore
@@ -1,51 +1,5 @@
 // Baseline format: 1.0
-AddedClass: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder:
-    Added class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder
-
-
-AddedField: androidx.core.view.accessibility.AccessibilityEventCompat#CONTENT_CHANGE_TYPE_CONTENT_INVALID:
-    Added field androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_CONTENT_INVALID
-AddedField: androidx.core.view.accessibility.AccessibilityEventCompat#CONTENT_CHANGE_TYPE_ENABLED:
-    Added field androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_ENABLED
-AddedField: androidx.core.view.accessibility.AccessibilityEventCompat#CONTENT_CHANGE_TYPE_ERROR:
-    Added field androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_ERROR
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_ANCESTORS:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_ANCESTORS
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_DESCENDANTS_HYBRID:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_HYBRID
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_SIBLINGS:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_SIBLINGS
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_UNINTERRUPTIBLE:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_UNINTERRUPTIBLE
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#MAX_NUMBER_OF_PREFETCHED_NODES:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.MAX_NUMBER_OF_PREFETCHED_NODES
-AddedField: androidx.core.view.accessibility.AccessibilityWindowInfoCompat#TYPE_MAGNIFICATION_OVERLAY:
-    Added field androidx.core.view.accessibility.AccessibilityWindowInfoCompat.TYPE_MAGNIFICATION_OVERLAY
-
-
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getBoundsInWindow(android.graphics.Rect):
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getBoundsInWindow(android.graphics.Rect)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getChild(int, int):
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getChild(int,int)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getContainerTitle():
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getContainerTitle()
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getParent(int):
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getParent(int)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#setBoundsInWindow(android.graphics.Rect):
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.setBoundsInWindow(android.graphics.Rect)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#setContainerTitle(CharSequence):
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.setContainerTitle(CharSequence)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#setQueryFromAppProcessEnabled(android.view.View, boolean):
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.setQueryFromAppProcessEnabled(android.view.View,boolean)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat#getColumnTitle():
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.getColumnTitle()
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat#getRowTitle():
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.getRowTitle()
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat#RangeInfoCompat(int, float, float, float):
-    Added constructor androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat(int,float,float,float)
-AddedMethod: androidx.core.view.accessibility.AccessibilityWindowInfoCompat#getRoot(int):
-    Added method androidx.core.view.accessibility.AccessibilityWindowInfoCompat.getRoot(int)
+AddedMethod: androidx.core.util.TypedValueCompat#getUnitFromComplexDimension(int):
+    Added method androidx.core.util.TypedValueCompat.getUnitFromComplexDimension(int)
+AddedMethod: androidx.core.widget.TextViewCompat#setLineHeight(android.widget.TextView, int, float):
+    Added method androidx.core.widget.TextViewCompat.setLineHeight(android.widget.TextView,int,float)
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 4bb3fb0..7ff5c2a 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -2390,6 +2390,7 @@
   public class TypedValueCompat {
     method public static float deriveDimension(int, float, android.util.DisplayMetrics);
     method public static float dpToPx(float, android.util.DisplayMetrics);
+    method public static int getUnitFromComplexDimension(int);
     method public static float pxToDp(float, android.util.DisplayMetrics);
     method public static float pxToSp(float, android.util.DisplayMetrics);
     method public static float spToPx(float, android.util.DisplayMetrics);
@@ -4199,6 +4200,7 @@
     method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
     method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
     method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLineHeight(android.widget.TextView, int, @FloatRange(from=0) float);
     method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
     method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
     method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
diff --git a/core/core/api/restricted_1.12.0-beta01.txt b/core/core/api/restricted_1.12.0-beta01.txt
index 102534f..7be35d5 100644
--- a/core/core/api/restricted_1.12.0-beta01.txt
+++ b/core/core/api/restricted_1.12.0-beta01.txt
@@ -2814,6 +2814,7 @@
   public class TypedValueCompat {
     method public static float deriveDimension(int, float, android.util.DisplayMetrics);
     method public static float dpToPx(float, android.util.DisplayMetrics);
+    method public static int getUnitFromComplexDimension(int);
     method public static float pxToDp(float, android.util.DisplayMetrics);
     method public static float pxToSp(float, android.util.DisplayMetrics);
     method public static float spToPx(float, android.util.DisplayMetrics);
@@ -4700,6 +4701,7 @@
     method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
     method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
     method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLineHeight(android.widget.TextView, int, @FloatRange(from=0) float);
     method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
     method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
     method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
diff --git a/core/core/api/restricted_current.ignore b/core/core/api/restricted_current.ignore
index f623e8e..a3517f0 100644
--- a/core/core/api/restricted_current.ignore
+++ b/core/core/api/restricted_current.ignore
@@ -1,51 +1,5 @@
 // Baseline format: 1.0
-AddedClass: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder:
-    Added class androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder
-
-
-AddedField: androidx.core.view.accessibility.AccessibilityEventCompat#CONTENT_CHANGE_TYPE_CONTENT_INVALID:
-    Added field androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_CONTENT_INVALID
-AddedField: androidx.core.view.accessibility.AccessibilityEventCompat#CONTENT_CHANGE_TYPE_ENABLED:
-    Added field androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_ENABLED
-AddedField: androidx.core.view.accessibility.AccessibilityEventCompat#CONTENT_CHANGE_TYPE_ERROR:
-    Added field androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_ERROR
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_ANCESTORS:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_ANCESTORS
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_DESCENDANTS_HYBRID:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_HYBRID
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_SIBLINGS:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_SIBLINGS
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#FLAG_PREFETCH_UNINTERRUPTIBLE:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FLAG_PREFETCH_UNINTERRUPTIBLE
-AddedField: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#MAX_NUMBER_OF_PREFETCHED_NODES:
-    Added field androidx.core.view.accessibility.AccessibilityNodeInfoCompat.MAX_NUMBER_OF_PREFETCHED_NODES
-AddedField: androidx.core.view.accessibility.AccessibilityWindowInfoCompat#TYPE_MAGNIFICATION_OVERLAY:
-    Added field androidx.core.view.accessibility.AccessibilityWindowInfoCompat.TYPE_MAGNIFICATION_OVERLAY
-
-
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getBoundsInWindow(android.graphics.Rect):
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getBoundsInWindow(android.graphics.Rect)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getChild(int, int):
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getChild(int,int)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getContainerTitle():
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getContainerTitle()
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getParent(int):
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.getParent(int)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#setBoundsInWindow(android.graphics.Rect):
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.setBoundsInWindow(android.graphics.Rect)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#setContainerTitle(CharSequence):
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.setContainerTitle(CharSequence)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#setQueryFromAppProcessEnabled(android.view.View, boolean):
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.setQueryFromAppProcessEnabled(android.view.View,boolean)
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat#getColumnTitle():
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.getColumnTitle()
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat#getRowTitle():
-    Added method androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.getRowTitle()
-AddedMethod: androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat#RangeInfoCompat(int, float, float, float):
-    Added constructor androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat(int,float,float,float)
-AddedMethod: androidx.core.view.accessibility.AccessibilityWindowInfoCompat#getRoot(int):
-    Added method androidx.core.view.accessibility.AccessibilityWindowInfoCompat.getRoot(int)
+AddedMethod: androidx.core.util.TypedValueCompat#getUnitFromComplexDimension(int):
+    Added method androidx.core.util.TypedValueCompat.getUnitFromComplexDimension(int)
+AddedMethod: androidx.core.widget.TextViewCompat#setLineHeight(android.widget.TextView, int, float):
+    Added method androidx.core.widget.TextViewCompat.setLineHeight(android.widget.TextView,int,float)
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 102534f..7be35d5 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -2814,6 +2814,7 @@
   public class TypedValueCompat {
     method public static float deriveDimension(int, float, android.util.DisplayMetrics);
     method public static float dpToPx(float, android.util.DisplayMetrics);
+    method public static int getUnitFromComplexDimension(int);
     method public static float pxToDp(float, android.util.DisplayMetrics);
     method public static float pxToSp(float, android.util.DisplayMetrics);
     method public static float spToPx(float, android.util.DisplayMetrics);
@@ -4700,6 +4701,7 @@
     method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
     method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
     method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLineHeight(android.widget.TextView, int, @FloatRange(from=0) float);
     method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
     method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
     method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
diff --git a/core/core/build.gradle b/core/core/build.gradle
index 149662a..967e12e 100644
--- a/core/core/build.gradle
+++ b/core/core/build.gradle
@@ -57,6 +57,7 @@
     androidTestImplementation(project(":internal-testutils-mockito"))
 
     testImplementation(libs.junit)
+    testImplementation(libs.testExtJunit)
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
     testImplementation(libs.truth)
diff --git a/core/core/src/androidTest/java/androidx/core/math/MathUtilsTest.java b/core/core/src/androidTest/java/androidx/core/math/MathUtilsTest.java
index 856acd4..a717d3d 100644
--- a/core/core/src/androidTest/java/androidx/core/math/MathUtilsTest.java
+++ b/core/core/src/androidTest/java/androidx/core/math/MathUtilsTest.java
@@ -17,7 +17,6 @@
 package androidx.core.math;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThrows;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -30,78 +29,6 @@
 public class MathUtilsTest {
 
     @Test
-    public void testAddExact() {
-        assertEquals(2, MathUtils.addExact(1, 1));
-        assertEquals(2L, MathUtils.addExact(1L, 1L));
-        assertEquals(0, MathUtils.addExact(1, -1));
-        assertEquals(0L, MathUtils.addExact(1L, -1L));
-        assertThrows(ArithmeticException.class, () -> MathUtils.addExact(Integer.MAX_VALUE, 1));
-        assertThrows(ArithmeticException.class, () -> MathUtils.addExact(Long.MAX_VALUE, 1L));
-        assertThrows(ArithmeticException.class, () -> MathUtils.addExact(Integer.MIN_VALUE, -1));
-        assertThrows(ArithmeticException.class, () -> MathUtils.addExact(Long.MIN_VALUE, -1L));
-    }
-
-    @Test
-    public void testSubtractExact() {
-        assertEquals(0, MathUtils.subtractExact(1, 1));
-        assertEquals(0L, MathUtils.subtractExact(1L, 1L));
-        assertEquals(2, MathUtils.subtractExact(1, -1));
-        assertEquals(2L, MathUtils.subtractExact(1L, -1L));
-        assertThrows(ArithmeticException.class,
-                () -> MathUtils.subtractExact(Integer.MAX_VALUE, -1));
-        assertThrows(ArithmeticException.class, () -> MathUtils.subtractExact(Long.MAX_VALUE, -1L));
-        assertThrows(ArithmeticException.class,
-                () -> MathUtils.subtractExact(Integer.MIN_VALUE, 1));
-        assertThrows(ArithmeticException.class, () -> MathUtils.subtractExact(Long.MIN_VALUE, 1L));
-    }
-
-    @Test
-    public void testMultiplyExact() {
-        assertEquals(4, MathUtils.multiplyExact(2, 2));
-        assertEquals(4L, MathUtils.multiplyExact(2L, 2L));
-        assertEquals(0, MathUtils.multiplyExact(2, 0));
-        assertEquals(0L, MathUtils.multiplyExact(2L, 0L));
-        assertEquals(-4, MathUtils.multiplyExact(2, -2));
-        assertEquals(-4L, MathUtils.multiplyExact(2L, -2L));
-        assertThrows(ArithmeticException.class,
-                () -> MathUtils.multiplyExact(Integer.MAX_VALUE, 2));
-        assertThrows(ArithmeticException.class, () -> MathUtils.multiplyExact(Long.MAX_VALUE, 2L));
-        assertThrows(ArithmeticException.class,
-                () -> MathUtils.multiplyExact(Integer.MIN_VALUE, 2));
-        assertThrows(ArithmeticException.class, () -> MathUtils.multiplyExact(Long.MIN_VALUE, 2L));
-    }
-
-    @Test
-    public void testIncrementExact() {
-        assertEquals(1, MathUtils.incrementExact(0));
-        assertEquals(1L, MathUtils.incrementExact(0L));
-        assertThrows(ArithmeticException.class, () -> MathUtils.incrementExact(Integer.MAX_VALUE));
-        assertThrows(ArithmeticException.class, () -> MathUtils.incrementExact(Long.MAX_VALUE));
-    }
-
-    @Test
-    public void testDecrementExact() {
-        assertEquals(-1, MathUtils.decrementExact(0));
-        assertEquals(-1L, MathUtils.decrementExact(0L));
-        assertThrows(ArithmeticException.class, () -> MathUtils.decrementExact(Integer.MIN_VALUE));
-        assertThrows(ArithmeticException.class, () -> MathUtils.decrementExact(Long.MIN_VALUE));
-    }
-
-    @Test
-    public void testNegateExact() {
-        assertEquals(Integer.MIN_VALUE + 1, MathUtils.negateExact(Integer.MAX_VALUE));
-        assertEquals(Long.MIN_VALUE + 1, MathUtils.negateExact(Long.MAX_VALUE));
-        assertThrows(ArithmeticException.class, () -> MathUtils.negateExact(Integer.MIN_VALUE));
-        assertThrows(ArithmeticException.class, () -> MathUtils.negateExact(Long.MIN_VALUE));
-    }
-
-    @Test
-    public void testToIntExact() {
-        assertEquals(1, MathUtils.toIntExact(1L));
-        assertThrows(ArithmeticException.class, () -> MathUtils.toIntExact(Long.MAX_VALUE));
-    }
-
-    @Test
     public void testClamp() {
         // Int
         assertEquals(0, MathUtils.clamp(-4, 0, 7));
diff --git a/core/core/src/main/java/androidx/core/math/MathUtils.java b/core/core/src/main/java/androidx/core/math/MathUtils.java
index 8a42777..f6129cc 100644
--- a/core/core/src/main/java/androidx/core/math/MathUtils.java
+++ b/core/core/src/main/java/androidx/core/math/MathUtils.java
@@ -24,172 +24,225 @@
     private MathUtils() {}
 
     /**
-     * See {@link Math#addExact(int, int)}.
+     * Returns the sum of its arguments, throwing an exception if the result overflows an
+     * {@code int}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
      */
     public static int addExact(int x, int y) {
-        // copied from Math.java
-        int r = x + y;
-        // HD 2-12 Overflow iff both arguments have the opposite sign of the result
-        if (((x ^ r) & (y ^ r)) < 0) {
+        int sum = x + y;
+        // If x and y have the same sign, their sum should have the same sign as well
+        if ((x >= 0 == y >= 0) && (x >= 0 != sum >= 0)) {
             throw new ArithmeticException("integer overflow");
+        } else {
+            return sum;
         }
-        return r;
     }
 
     /**
-     * See {@link Math#addExact(long, long)}.
+     * Returns the sum of its arguments, throwing an exception if the result overflows a
+     * {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
      */
     public static long addExact(long x, long y) {
-        // copied from Math.java
-        long r = x + y;
-        // HD 2-12 Overflow iff both arguments have the opposite sign of the result
-        if (((x ^ r) & (y ^ r)) < 0) {
-            throw new ArithmeticException("long overflow");
+        long sum = x + y;
+        // If x and y have the same sign, their sum should have the same sign as well
+        if ((x >= 0 == y >= 0) && (x >= 0 != sum >= 0)) {
+            throw new ArithmeticException("integer overflow");
+        } else {
+            return sum;
         }
-        return r;
     }
 
-
     /**
-     * See {@link Math#subtractExact(int, int)}.
+     * Returns the difference of the arguments, throwing an exception if the result overflows an
+     * {@code int}.
+     *
+     * @param x the first value
+     * @param y the second value to subtract from the first
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
      */
     public static int subtractExact(int x, int y) {
-        // copied from Math.java
-        int r = x - y;
-        // HD 2-12 Overflow iff the arguments have different signs and
-        // the sign of the result is different than the sign of x
-        if (((x ^ y) & (x ^ r)) < 0) {
+        int difference = x - y;
+        // If only one of x or y is negative, the difference should have the same sign as x
+        if ((x < 0 != y < 0) && (x < 0 != difference < 0)) {
             throw new ArithmeticException("integer overflow");
         }
-        return r;
+        return difference;
     }
 
     /**
-     * See {@link Math#subtractExact(long, long)}.
+     * Returns the difference of the arguments, throwing an exception if the result overflows a
+     * {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value to subtract from the first
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
      */
     public static long subtractExact(long x, long y) {
-        // copied from Math.java
-        long r = x - y;
-        // HD 2-12 Overflow iff the arguments have different signs and
-        // the sign of the result is different than the sign of x
-        if (((x ^ y) & (x ^ r)) < 0) {
-            throw new ArithmeticException("long overflow");
+        long difference = x - y;
+        // If only one of x or y is negative, the difference should have the same sign as x
+        if ((x < 0 != y < 0) && (x < 0 != difference < 0)) {
+            throw new ArithmeticException("integer overflow");
         }
-        return r;
+        return difference;
     }
 
     /**
-     * See {@link Math#multiplyExact(int, int)}.
+     * Returns the product of the arguments, throwing an exception if the result overflows an
+     * {@code int}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
      */
     public static int multiplyExact(int x, int y) {
-        // copied from Math.java
-        long r = (long) x * (long) y;
-        if ((int) r != r) {
+        int product = x * y;
+        // Dividing back by one of x or y should get the other back unless there was overflow
+        if (x != 0 && y != 0 && (product / x != y || product / y != x)) {
             throw new ArithmeticException("integer overflow");
         }
-        return (int) r;
+        return product;
     }
 
     /**
-     * See {@link Math#multiplyExact(long, long)}.
+     * Returns the product of the arguments, throwing an exception if the result overflows a
+     * {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
      */
     public static long multiplyExact(long x, long y) {
-        // copied from Math.java
-        long r = x * y;
-        long ax = Math.abs(x);
-        long ay = Math.abs(y);
-        if (((ax | ay) >>> 31 != 0)) {
-            // Some bits greater than 2^31 that might cause overflow
-            // Check the result using the divide operator
-            // and check for the special case of Long.MIN_VALUE * -1
-            if (((y != 0) && (r / y != x)) || (x == Long.MIN_VALUE && y == -1)) {
-                throw new ArithmeticException("long overflow");
-            }
+        long product = x * y;
+        // Dividing back by one of x or y should get the other back unless there was overflow
+        if (x != 0 && y != 0 && (product / x != y || product / y != x)) {
+            throw new ArithmeticException("integer overflow");
         }
-        return r;
+        return product;
     }
 
     /**
-     * See {@link Math#incrementExact(int)}.
+     * Returns the argument incremented by one, throwing an exception if the result overflows an
+     * {@code int}. The overflow only occurs for {@linkplain Integer#MAX_VALUE the maximum value}.
+     *
+     * @param a the value to increment
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
      */
     public static int incrementExact(int a) {
-        // copied from Math.java
         if (a == Integer.MAX_VALUE) {
             throw new ArithmeticException("integer overflow");
+        } else {
+            return a + 1;
         }
-
-        return a + 1;
     }
 
     /**
-     * See {@link Math#incrementExact(long)}.
+     * Returns the argument incremented by one, throwing an exception if the result overflows a
+     * {@code long}. The overflow only occurs for {@linkplain Long#MAX_VALUE the maximum value}.
+     *
+     * @param a the value to increment
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
      */
     public static long incrementExact(long a) {
-        // copied from Math.java
         if (a == Long.MAX_VALUE) {
-            throw new ArithmeticException("long overflow");
+            throw new ArithmeticException("integer overflow");
+        } else {
+            return a + 1;
         }
-
-        return a + 1L;
     }
 
     /**
-     * See {@link Math#decrementExact(int)}.
+     * Returns the argument decremented by one, throwing an exception if the result overflows an
+     * {@code int}. The overflow only occurs for {@linkplain Integer#MIN_VALUE the minimum value}.
+     *
+     * @param a the value to decrement
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
      */
     public static int decrementExact(int a) {
-        // copied from Math.java
         if (a == Integer.MIN_VALUE) {
             throw new ArithmeticException("integer overflow");
+        } else {
+            return a - 1;
         }
-
-        return a - 1;
     }
 
     /**
-     * See {@link Math#decrementExact(long)}.
+     * Returns the argument decremented by one, throwing an exception if the result overflows a
+     * {@code long}. The overflow only occurs for {@linkplain Long#MIN_VALUE the minimum value}.
+     *
+     * @param a the value to decrement
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
      */
     public static long decrementExact(long a) {
-        // copied from Math.java
         if (a == Long.MIN_VALUE) {
-            throw new ArithmeticException("long overflow");
+            throw new ArithmeticException("integer overflow");
+        } else {
+            return a - 1;
         }
-
-        return a - 1L;
     }
 
     /**
-     * See {@link Math#negateExact(int)}.
+     * Returns the negation of the argument, throwing an exception if the result overflows an
+     * {@code int}. The overflow only occurs for {@linkplain Integer#MIN_VALUE the minimum value}.
+     *
+     * @param a the value to negate
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
      */
     public static int negateExact(int a) {
-        // copied from Math.java
         if (a == Integer.MIN_VALUE) {
             throw new ArithmeticException("integer overflow");
+        } else {
+            return -a;
         }
-
-        return -a;
     }
 
     /**
-     * See {@link Math#negateExact(long)}.
+     * Returns the negation of the argument, throwing an exception if the result overflows a
+     * {@code long}. The overflow only occurs for {@linkplain Long#MIN_VALUE the minimum value}.
+     *
+     * @param a the value to negate
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
      */
     public static long negateExact(long a) {
-        // copied from Math.java
         if (a == Long.MIN_VALUE) {
-            throw new ArithmeticException("long overflow");
+            throw new ArithmeticException("integer overflow");
+        } else {
+            return -a;
         }
-
-        return -a;
     }
 
     /**
-     * See {@link Math#toIntExact(long)}.
+     * Returns the value of the {@code long} argument, throwing an exception if the value
+     * overflows an {@code int}.
+     *
+     * @param value the long value
+     * @return the argument as an int
+     * @throws ArithmeticException if the {@code argument} overflows an int
      */
     public static int toIntExact(long value) {
-        // copied from Math.java
-        if ((int) value != value) {
+        if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
             throw new ArithmeticException("integer overflow");
+        } else {
+            return (int) value;
         }
-        return (int) value;
     }
 
     /**
diff --git a/core/core/src/main/java/androidx/core/util/TypedValueCompat.java b/core/core/src/main/java/androidx/core/util/TypedValueCompat.java
index 49f3bab..55f102d 100644
--- a/core/core/src/main/java/androidx/core/util/TypedValueCompat.java
+++ b/core/core/src/main/java/androidx/core/util/TypedValueCompat.java
@@ -44,6 +44,17 @@
     private TypedValueCompat() {}
 
     /**
+     * Return the complex unit type for the given complex dimension. For example, a dimen type
+     * with value 12sp will return {@link TypedValue#COMPLEX_UNIT_SP}.
+     *
+     * @param complexDimension the dimension, typically {@link TypedValue#data}
+     * @return The complex unit type
+     */
+    public static int getUnitFromComplexDimension(int complexDimension) {
+        return TypedValue.COMPLEX_UNIT_MASK & (complexDimension >> TypedValue.COMPLEX_UNIT_SHIFT);
+    }
+
+    /**
      * Converts a pixel value to the given dimension, e.g. PX to DP.
      *
      * <p>This is the inverse of {@link TypedValue#applyDimension(int, float, DisplayMetrics)}
diff --git a/core/core/src/main/java/androidx/core/widget/TextViewCompat.java b/core/core/src/main/java/androidx/core/widget/TextViewCompat.java
index 915d0af2..0e58694 100644
--- a/core/core/src/main/java/androidx/core/widget/TextViewCompat.java
+++ b/core/core/src/main/java/androidx/core/widget/TextViewCompat.java
@@ -54,6 +54,7 @@
 
 import androidx.annotation.DoNotInline;
 import androidx.annotation.DrawableRes;
+import androidx.annotation.FloatRange;
 import androidx.annotation.IntDef;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
@@ -806,6 +807,7 @@
      * Sets an explicit line height for this TextView. This is equivalent to the vertical distance
      * between subsequent baselines in the TextView.
      *
+     * @param textView the TextView to modify
      * @param lineHeight the line height in pixels
      *
      * @see TextView#setLineSpacing(float, float)
@@ -827,6 +829,38 @@
     }
 
     /**
+     * Sets an explicit line height to a given unit and value for the TextView. This is equivalent
+     * to the vertical distance between subsequent baselines in the TextView. See {@link
+     * TypedValue} for the possible dimension units.
+     *
+     * @param textView the TextView to modify
+     * @param unit The desired dimension unit. SP units are strongly recommended so that line height
+     *             stays proportional to the text size when fonts are scaled up for accessibility.
+     * @param lineHeight The desired line height in the given units.
+     *
+     * @see TextView#setLineSpacing(float, float)
+     * @see TextView#getLineSpacingExtra()
+     *
+     * @attr ref android.R.styleable#TextView_lineHeight
+     */
+    public static void setLineHeight(
+            @NonNull TextView textView,
+            int unit,
+            @FloatRange(from = 0) float lineHeight
+    ) {
+        if (Build.VERSION.SDK_INT >= 34) {
+            Api34Impl.setLineHeight(textView, unit, lineHeight);
+        } else {
+            float lineHeightPx = TypedValue.applyDimension(
+                    unit,
+                    lineHeight,
+                    textView.getResources().getDisplayMetrics()
+            );
+            setLineHeight(textView, Math.round(lineHeightPx));
+        }
+    }
+
+    /**
      * Gets the parameters for text layout precomputation, for use with
      * {@link PrecomputedTextCompat}.
      *
@@ -1283,4 +1317,20 @@
             return DecimalFormatSymbols.getInstance(locale);
         }
     }
+
+    @RequiresApi(34)
+    static class Api34Impl {
+        private Api34Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        public static void setLineHeight(
+                @NonNull TextView textView,
+                int unit,
+                @FloatRange(from = 0) float lineHeight
+        ) {
+            textView.setLineHeight(unit, lineHeight);
+        }
+    }
 }
diff --git a/core/core/src/test/java/androidx/core/math/MathUtilsTest.kt b/core/core/src/test/java/androidx/core/math/MathUtilsTest.kt
new file mode 100644
index 0000000..3074b56
--- /dev/null
+++ b/core/core/src/test/java/androidx/core/math/MathUtilsTest.kt
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.core.math
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class MathUtilsTest {
+
+    @Test
+    fun testAddExact() {
+        // zero + zero
+        Assert.assertEquals(0, MathUtils.addExact(0, 0).toLong())
+        Assert.assertEquals(0L, MathUtils.addExact(0L, 0L))
+        // positive + positive
+        Assert.assertEquals(2, MathUtils.addExact(1, 1).toLong())
+        Assert.assertEquals(2L, MathUtils.addExact(1L, 1L))
+        // negative + negative
+        Assert.assertEquals(-2, MathUtils.addExact(-1, -1).toLong())
+        Assert.assertEquals(-2L, MathUtils.addExact(-1L, -1L))
+        // positive + negative
+        Assert.assertEquals(0, MathUtils.addExact(1, -1).toLong())
+        Assert.assertEquals(0L, MathUtils.addExact(1L, -1L))
+        Assert.assertEquals(-1, MathUtils.addExact(1, -2).toLong())
+        Assert.assertEquals(-1L, MathUtils.addExact(1L, -2L))
+        Assert.assertEquals(1, MathUtils.addExact(2, -1).toLong())
+        Assert.assertEquals(1L, MathUtils.addExact(2L, -1L))
+        // negative + positive
+        Assert.assertEquals(0, MathUtils.addExact(-1, 1).toLong())
+        Assert.assertEquals(0L, MathUtils.addExact(-1L, 1L))
+        Assert.assertEquals(1, MathUtils.addExact(-1, 2).toLong())
+        Assert.assertEquals(1L, MathUtils.addExact(-1L, 2L))
+        Assert.assertEquals(-1, MathUtils.addExact(-2, 1).toLong())
+        Assert.assertEquals(-1L, MathUtils.addExact(-2L, 1L))
+        // zero + positive, positive + zero
+        Assert.assertEquals(1, MathUtils.addExact(0, 1).toLong())
+        Assert.assertEquals(1L, MathUtils.addExact(0L, 1L))
+        Assert.assertEquals(1, MathUtils.addExact(1, 0).toLong())
+        Assert.assertEquals(1L, MathUtils.addExact(1L, 0L))
+        // zero + negative, negative + zero
+        Assert.assertEquals(-1, MathUtils.addExact(0, -1).toLong())
+        Assert.assertEquals(-1L, MathUtils.addExact(0L, -1L))
+        Assert.assertEquals(-1, MathUtils.addExact(-1, 0).toLong())
+        Assert.assertEquals(-1L, MathUtils.addExact(-1L, 0L))
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Int.MAX_VALUE,
+                1
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Long.MAX_VALUE,
+                1L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Int.MIN_VALUE,
+                -1
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Long.MIN_VALUE,
+                -1L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Integer.MIN_VALUE,
+                Integer.MIN_VALUE
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Long.MIN_VALUE,
+                Long.MIN_VALUE
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Integer.MAX_VALUE,
+                Integer.MAX_VALUE
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Long.MAX_VALUE,
+                Long.MAX_VALUE
+            )
+        }
+    }
+
+    @Test
+    fun testSubtractExact() {
+        // zero - zero
+        Assert.assertEquals(0, MathUtils.subtractExact(0, 0).toLong())
+        Assert.assertEquals(0L, MathUtils.subtractExact(0L, 0L))
+        // positive - positive
+        Assert.assertEquals(0, MathUtils.subtractExact(1, 1).toLong())
+        Assert.assertEquals(0L, MathUtils.subtractExact(1L, 1L))
+        Assert.assertEquals(1, MathUtils.subtractExact(2, 1).toLong())
+        Assert.assertEquals(1L, MathUtils.subtractExact(2L, 1L))
+        Assert.assertEquals(-1, MathUtils.subtractExact(1, 2).toLong())
+        Assert.assertEquals(-1L, MathUtils.subtractExact(1L, 2L))
+        // negative - negative
+        Assert.assertEquals(0, MathUtils.subtractExact(-1, -1).toLong())
+        Assert.assertEquals(0L, MathUtils.subtractExact(-1L, -1L))
+        Assert.assertEquals(-1, MathUtils.subtractExact(-2, -1).toLong())
+        Assert.assertEquals(-1L, MathUtils.subtractExact(-2L, -1L))
+        Assert.assertEquals(1, MathUtils.subtractExact(-1, -2).toLong())
+        Assert.assertEquals(1L, MathUtils.subtractExact(-1L, -2L))
+        // positive - negative, negative - positive
+        Assert.assertEquals(2, MathUtils.subtractExact(1, -1).toLong())
+        Assert.assertEquals(2L, MathUtils.subtractExact(1L, -1L))
+        Assert.assertEquals(-2, MathUtils.subtractExact(-1, 1).toLong())
+        Assert.assertEquals(-2L, MathUtils.subtractExact(-1L, 1L))
+        // zero - positive, positive - zero
+        Assert.assertEquals(-1, MathUtils.subtractExact(0, 1).toLong())
+        Assert.assertEquals(-1L, MathUtils.subtractExact(0L, 1L))
+        Assert.assertEquals(1, MathUtils.subtractExact(1, 0).toLong())
+        Assert.assertEquals(1L, MathUtils.subtractExact(1L, 0L))
+        // zero - negative, negative - zero
+        Assert.assertEquals(1, MathUtils.subtractExact(0, -1).toLong())
+        Assert.assertEquals(1L, MathUtils.subtractExact(0L, -1L))
+        Assert.assertEquals(-1, MathUtils.subtractExact(-1, 0).toLong())
+        Assert.assertEquals(-1L, MathUtils.subtractExact(-1L, 0))
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.subtractExact(
+                Int.MAX_VALUE,
+                -1
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.subtractExact(
+                Long.MAX_VALUE,
+                -1L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.subtractExact(
+                Int.MIN_VALUE,
+                1
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.subtractExact(
+                Long.MIN_VALUE,
+                1L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.subtractExact(
+                0,
+                Int.MIN_VALUE
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.subtractExact(
+                0,
+                Long.MIN_VALUE
+            )
+        }
+    }
+
+    @Test
+    fun testMultiplyExact() {
+        Assert.assertEquals(0, MathUtils.multiplyExact(0, 0).toLong())
+        Assert.assertEquals(4, MathUtils.multiplyExact(2, 2).toLong())
+        Assert.assertEquals(0L, MathUtils.multiplyExact(0L, 0L))
+        Assert.assertEquals(4L, MathUtils.multiplyExact(2L, 2L))
+        Assert.assertEquals(0, MathUtils.multiplyExact(2, 0).toLong())
+        Assert.assertEquals(0L, MathUtils.multiplyExact(2L, 0L))
+        Assert.assertEquals(-4, MathUtils.multiplyExact(2, -2).toLong())
+        Assert.assertEquals(-4L, MathUtils.multiplyExact(2L, -2L))
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Int.MAX_VALUE,
+                2
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Long.MAX_VALUE,
+                2L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Int.MIN_VALUE,
+                2
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Long.MIN_VALUE,
+                2L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Int.MAX_VALUE / 2 + 1,
+                2
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Long.MAX_VALUE / 2L + 1L,
+                2L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Int.MIN_VALUE / 2 - 1,
+                2
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Long.MIN_VALUE / 2L - 1L,
+                2L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Int.MIN_VALUE,
+                -1
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Long.MIN_VALUE,
+                -1L
+            )
+        }
+    }
+
+    @Test
+    fun testIncrementExact() {
+        Assert.assertEquals(1, MathUtils.incrementExact(0).toLong())
+        Assert.assertEquals(1L, MathUtils.incrementExact(0L))
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.incrementExact(Int.MAX_VALUE) }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.incrementExact(Long.MAX_VALUE) }
+    }
+
+    @Test
+    fun testDecrementExact() {
+        Assert.assertEquals(-1, MathUtils.decrementExact(0).toLong())
+        Assert.assertEquals(-1L, MathUtils.decrementExact(0L))
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.decrementExact(Int.MIN_VALUE) }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.decrementExact(Long.MIN_VALUE) }
+    }
+
+    @Test
+    fun testNegateExact() {
+        Assert.assertEquals(
+            (Int.MIN_VALUE + 1).toLong(),
+            MathUtils.negateExact(Int.MAX_VALUE).toLong()
+        )
+        Assert.assertEquals(Long.MIN_VALUE + 1, MathUtils.negateExact(Long.MAX_VALUE))
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.negateExact(Int.MIN_VALUE) }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.negateExact(Long.MIN_VALUE) }
+    }
+
+    @Test
+    fun testToIntExact() {
+        Assert.assertEquals(1, MathUtils.toIntExact(1L).toLong())
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.toIntExact(Long.MAX_VALUE) }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.toIntExact(Long.MIN_VALUE) }
+    }
+}
diff --git a/credentials/credentials-play-services-auth/src/androidTest/AndroidManifest.xml b/credentials/credentials-play-services-auth/src/androidTest/AndroidManifest.xml
index 73badf7..d7b0e91 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/AndroidManifest.xml
+++ b/credentials/credentials-play-services-auth/src/androidTest/AndroidManifest.xml
@@ -21,9 +21,5 @@
             android:name="androidx.credentials.playservices.TestCredentialsActivity"
             android:exported="false"
             />
-        <activity
-            android:name="androidx.credentials.playservices.TestCredentialsFragmentActivity"
-            android:exported="false"
-            />
     </application>
 </manifest>
\ No newline at end of file
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/TestCredentialsFragmentActivity.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/TestCredentialsFragmentActivity.kt
deleted file mode 100644
index f7ad106..0000000
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/TestCredentialsFragmentActivity.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2023 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.credentials.playservices
-
-import android.os.Build
-import androidx.annotation.RequiresApi
-import androidx.fragment.app.FragmentActivity
-
-/**
- * This is a test activity used by the Robolectric Activity Scenario tests. It acts as a calling
- * activity in our test cases. This activity uses fragments.
- */
-@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
-class TestCredentialsFragmentActivity : FragmentActivity()
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
index acbfe57..e4cddda 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
@@ -20,14 +20,12 @@
 
 import static org.junit.Assert.assertThrows;
 
-import android.app.Activity;
-import android.os.Build;
-
 import androidx.credentials.GetCredentialRequest;
 import androidx.credentials.GetPasswordOption;
 import androidx.credentials.playservices.TestCredentialsActivity;
 import androidx.credentials.playservices.controllers.BeginSignIn.CredentialProviderBeginSignInController;
 import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.google.android.gms.auth.api.identity.BeginSignInRequest;
@@ -35,56 +33,19 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
 import java.util.HashSet;
 import java.util.List;
 
-@RunWith(Parameterized.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 @SuppressWarnings("deprecation")
 public class CredentialProviderBeginSignInControllerJavaTest {
-
-    private final boolean mUseFragmentActivity;
-
-    @Parameterized.Parameters
-    public static Object[] data() {
-        return new Object[] {true, false};
-    }
-
-    public CredentialProviderBeginSignInControllerJavaTest(final boolean useFragmentActivity)
-            throws Throwable {
-        mUseFragmentActivity = useFragmentActivity;
-    }
-
-    interface TestActivityListener {
-        void onActivity(Activity a);
-    }
-
-    private void launchTestActivity(TestActivityListener listener) {
-        if (mUseFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            ActivityScenario<androidx.credentials.playservices.TestCredentialsFragmentActivity>
-                    activityScenario =
-                            ActivityScenario.launch(
-                                    androidx.credentials.playservices
-                                            .TestCredentialsFragmentActivity.class);
-            activityScenario.onActivity(
-                    activity -> {
-                        listener.onActivity((Activity) activity);
-                    });
-        } else {
-            ActivityScenario<TestCredentialsActivity> activityScenario =
-                    ActivityScenario.launch(TestCredentialsActivity.class);
-            activityScenario.onActivity(
-                    activity -> {
-                        listener.onActivity((Activity) activity);
-                    });
-        }
-    }
-
     @Test
     public void convertRequestToPlayServices_setPasswordOptionRequestAndFalseAutoSelect_success() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     BeginSignInRequest actualResponse =
                             CredentialProviderBeginSignInController.getInstance(activity)
@@ -99,7 +60,9 @@
 
     @Test
     public void convertRequestToPlayServices_setPasswordOptionRequestAndTrueAutoSelect_success() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     BeginSignInRequest actualResponse =
                             CredentialProviderBeginSignInController.getInstance(activity)
@@ -116,7 +79,9 @@
 
     @Test
     public void convertRequestToPlayServices_nullRequest_throws() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     assertThrows(
                             "null get credential request must throw exception",
@@ -129,7 +94,9 @@
 
     @Test
     public void convertResponseToCredentialManager_nullRequest_throws() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     assertThrows(
                             "null sign in credential response must throw exception",
@@ -142,6 +109,9 @@
 
     @Test
     public void convertRequestToPlayServices_setGoogleIdOptionRequestAndTrueAutoSelect_success() {
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+
         GetGoogleIdOption option =
                 new GetGoogleIdOption.Builder()
                         .setServerClientId("server_client_id")
@@ -152,7 +122,7 @@
                         .setAutoSelectEnabled(true)
                         .build();
 
-        launchTestActivity(
+        activityScenario.onActivity(
                 activity -> {
                     BeginSignInRequest actualRequest =
                             CredentialProviderBeginSignInController.getInstance(activity)
@@ -181,7 +151,9 @@
 
     @Test
     public void duplicateGetInstance_shouldBeEqual() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     CredentialProviderBeginSignInController firstInstance =
                             CredentialProviderBeginSignInController.getInstance(activity);
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
index 5e05605..d245c4b 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
@@ -16,55 +16,32 @@
 
 package androidx.credentials.playservices.beginsignin
 
-import android.app.Activity
 import android.os.Build
 import androidx.annotation.RequiresApi
 import androidx.credentials.GetCredentialRequest
 import androidx.credentials.GetPasswordOption
 import androidx.credentials.playservices.TestCredentialsActivity
-import androidx.credentials.playservices.TestCredentialsFragmentActivity
 import androidx.credentials.playservices.controllers.BeginSignIn.CredentialProviderBeginSignInController.Companion.getInstance
 import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.android.libraries.identity.googleid.GetGoogleIdOption
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
 
-@RunWith(Parameterized::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 @Suppress("deprecation")
 @RequiresApi(api = Build.VERSION_CODES.O)
-class CredentialProviderBeginSignInControllerTest(val useFragmentActivity: Boolean) {
-
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "{0}")
-        fun initParameters() = listOf(true, false)
-    }
-
-    private fun launchTestActivity(callback: (activity: Activity) -> Unit) {
-        if (useFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            var activityScenario =
-                            ActivityScenario.launch(
-                                    androidx.credentials.playservices
-                                            .TestCredentialsFragmentActivity::class.java)
-            activityScenario.onActivity { activity: Activity ->
-                callback.invoke(activity)
-            }
-        } else {
-            var activityScenario = ActivityScenario.launch(TestCredentialsActivity::class.java)
-            activityScenario.onActivity { activity: Activity ->
-                callback.invoke(activity)
-            }
-        }
-    }
-
+class CredentialProviderBeginSignInControllerTest {
     @Test
     fun convertRequestToPlayServices_setPasswordOptionRequestAndFalseAutoSelect_success() {
-        launchTestActivity { activity: Activity ->
-            val actualResponse = getInstance(activity)
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
+            val actualResponse = getInstance(activity!!)
                 .convertRequestToPlayServices(
                     GetCredentialRequest(
                         listOf(
@@ -81,8 +58,11 @@
 
     @Test
     fun convertRequestToPlayServices_setPasswordOptionRequestAndTrueAutoSelect_success() {
-        launchTestActivity { activity: Activity ->
-            val actualResponse = getInstance(activity)
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
+            val actualResponse = getInstance(activity!!)
                 .convertRequestToPlayServices(
                     GetCredentialRequest(
                         listOf(
@@ -99,6 +79,10 @@
 
     @Test
     fun convertRequestToPlayServices_setGoogleIdOptionRequest_success() {
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+
         val option = GetGoogleIdOption.Builder()
             .setServerClientId("server_client_id")
             .setNonce("nonce")
@@ -108,8 +92,8 @@
             .setAutoSelectEnabled(true)
             .build()
 
-        launchTestActivity { activity: Activity ->
-            val actualRequest = getInstance(activity)
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
+            val actualRequest = getInstance(activity!!)
                 .convertRequestToPlayServices(
                     GetCredentialRequest(
                         listOf(
@@ -136,9 +120,12 @@
 
     @Test
     fun duplicateGetInstance_shouldBeEqual() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
-            val firstInstance = getInstance(activity)
+            val firstInstance = getInstance(activity!!)
             val secondInstance = getInstance(activity)
             assertThat(firstInstance).isEqualTo(secondInstance)
         }
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerJavaTest.java b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerJavaTest.java
index f0d5a85..00b86ba 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerJavaTest.java
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerJavaTest.java
@@ -20,8 +20,6 @@
 
 import static org.junit.Assert.assertThrows;
 
-import android.app.Activity;
-import android.os.Build;
 import android.os.Bundle;
 
 import androidx.credentials.CreateCredentialResponse;
@@ -31,6 +29,7 @@
 import androidx.credentials.playservices.TestUtils;
 import androidx.credentials.playservices.controllers.CreatePassword.CredentialProviderCreatePasswordController;
 import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.google.android.gms.auth.api.identity.SignInPassword;
@@ -39,53 +38,17 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
-@RunWith(Parameterized.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class CredentialProviderCreatePasswordControllerJavaTest {
 
-    private final boolean mUseFragmentActivity;
-
-    @Parameterized.Parameters
-    public static Object[] data() {
-        return new Object[] {true, false};
-    }
-
-    public CredentialProviderCreatePasswordControllerJavaTest(final boolean useFragmentActivity)
-            throws Throwable {
-        mUseFragmentActivity = useFragmentActivity;
-    }
-
-    interface TestActivityListener {
-        void onActivity(Activity a);
-    }
-
-    private void launchTestActivity(TestActivityListener listener) {
-        if (mUseFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            ActivityScenario<androidx.credentials.playservices.TestCredentialsFragmentActivity>
-                    activityScenario =
-                            ActivityScenario.launch(
-                                    androidx.credentials.playservices
-                                            .TestCredentialsFragmentActivity.class);
-            activityScenario.onActivity(
-                    activity -> {
-                        listener.onActivity((Activity) activity);
-                    });
-        } else {
-            ActivityScenario<TestCredentialsActivity> activityScenario =
-                    ActivityScenario.launch(TestCredentialsActivity.class);
-            activityScenario.onActivity(
-                    activity -> {
-                        listener.onActivity((Activity) activity);
-                    });
-        }
-    }
-
     @Test
     public void convertResponseToCredentialManager_unitInput_success() {
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
         String expectedResponseType = new CreatePasswordResponse().getType();
-        launchTestActivity(
+        activityScenario.onActivity(
                 activity -> {
                     CreateCredentialResponse actualResponse =
                             CredentialProviderCreatePasswordController.getInstance(activity)
@@ -99,9 +62,11 @@
 
     @Test
     public void convertRequestToPlayServices_createPasswordRequest_success() {
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
         String expectedId = "LM";
         String expectedPassword = "SodaButton";
-        launchTestActivity(
+        activityScenario.onActivity(
                 activity -> {
                     SignInPassword actualRequest =
                             CredentialProviderCreatePasswordController.getInstance(activity)
@@ -118,7 +83,7 @@
     public void convertRequestToPlayServices_nullRequest_throws() {
         ActivityScenario<TestCredentialsActivity> activityScenario =
                 ActivityScenario.launch(TestCredentialsActivity.class);
-        launchTestActivity(
+        activityScenario.onActivity(
                 activity -> {
                     assertThrows(
                             "null create password request must throw exception",
@@ -132,7 +97,9 @@
 
     @Test
     public void convertResponseToCredentialManager_nullRequest_throws() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     assertThrows(
                             "null unit response must throw exception",
@@ -145,7 +112,9 @@
 
     @Test
     public void duplicateGetInstance_shouldBeEqual() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     CredentialProviderCreatePasswordController firstInstance =
                             CredentialProviderCreatePasswordController.getInstance(activity);
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerTest.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerTest.kt
index cc2e365..0710e37 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerTest.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerTest.kt
@@ -16,56 +16,31 @@
 
 package androidx.credentials.playservices.createpassword
 
-import android.app.Activity
-import android.os.Build
 import android.os.Bundle
-import androidx.annotation.DoNotInline
 import androidx.credentials.CreatePasswordRequest
 import androidx.credentials.CreatePasswordResponse
 import androidx.credentials.playservices.TestCredentialsActivity
 import androidx.credentials.playservices.TestUtils.Companion.equals
 import androidx.credentials.playservices.controllers.CreatePassword.CredentialProviderCreatePasswordController.Companion.getInstance
 import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
 
-@RunWith(Parameterized::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
-class CredentialProviderCreatePasswordControllerTest(val useFragmentActivity: Boolean) {
-
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "{0}")
-        fun initParameters() = listOf(true, false)
-    }
-
-    @DoNotInline
-    private fun launchTestActivity(callback: (activity: Activity) -> Unit) {
-        if (useFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            var activityScenario =
-                            ActivityScenario.launch(
-                                    androidx.credentials.playservices
-                                            .TestCredentialsFragmentActivity::class.java)
-            activityScenario.onActivity { activity: Activity ->
-                callback.invoke(activity)
-            }
-        } else {
-            var activityScenario = ActivityScenario.launch(TestCredentialsActivity::class.java)
-            activityScenario.onActivity { activity: Activity ->
-                callback.invoke(activity)
-            }
-        }
-    }
-
+class CredentialProviderCreatePasswordControllerTest {
     @Test
     fun convertResponseToCredentialManager_unitInput_success() {
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
         val expectedResponseType = CreatePasswordResponse().type
-        launchTestActivity { activity: Activity ->
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
-            val actualResponse = getInstance(activity)
+            val actualResponse = getInstance(activity!!)
                 .convertResponseToCredentialManager(Unit)
 
             assertThat(actualResponse.type)
@@ -76,11 +51,14 @@
 
     @Test
     fun convertRequestToPlayServices_createPasswordRequest_success() {
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
         val expectedId = "LM"
         val expectedPassword = "SodaButton"
-        launchTestActivity { activity: Activity ->
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
-            val actualRequest = getInstance(activity)
+            val actualRequest = getInstance(activity!!)
                 .convertRequestToPlayServices(CreatePasswordRequest(
                         expectedId, expectedPassword)).signInPassword
 
@@ -92,9 +70,12 @@
 
     @Test
     fun duplicateGetInstance_shouldBeEqual() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
-            val firstInstance = getInstance(activity)
+            val firstInstance = getInstance(activity!!)
             val secondInstance = getInstance(activity)
             assertThat(firstInstance).isEqualTo(secondInstance)
         }
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerJavaTest.java b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerJavaTest.java
index 48d07d7..a814d5f 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerJavaTest.java
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerJavaTest.java
@@ -16,6 +16,7 @@
 
 package androidx.credentials.playservices.createpublickeycredential;
 
+
 import static androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.ALL_REQUIRED_AND_OPTIONAL_SIGNATURE;
 import static androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.ALL_REQUIRED_FIELDS_SIGNATURE;
 import static androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT;
@@ -33,14 +34,12 @@
 
 import static org.junit.Assert.assertThrows;
 
-import android.app.Activity;
-import android.os.Build;
-
 import androidx.credentials.CreatePublicKeyCredentialRequest;
 import androidx.credentials.playservices.TestCredentialsActivity;
 import androidx.credentials.playservices.TestUtils;
 import androidx.credentials.playservices.controllers.CreatePublicKeyCredential.CredentialProviderCreatePublicKeyCredentialController;
 import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions;
@@ -49,212 +48,156 @@
 import org.json.JSONObject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
-@RunWith(Parameterized.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class CredentialProviderCreatePublicKeyCredentialControllerJavaTest {
-
-    private final boolean mUseFragmentActivity;
-
-    @Parameterized.Parameters
-    public static Object[] data() {
-        return new Object[] {true, false};
-    }
-
-    public CredentialProviderCreatePublicKeyCredentialControllerJavaTest(
-            final boolean useFragmentActivity) throws Throwable {
-        mUseFragmentActivity = useFragmentActivity;
-    }
-
-    interface TestActivityListener {
-        void onActivity(Activity a);
-    }
-
-    private void launchTestActivity(TestActivityListener listener) {
-        if (mUseFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            ActivityScenario<androidx.credentials.playservices.TestCredentialsFragmentActivity>
-                    activityScenario =
-                            ActivityScenario.launch(
-                                    androidx.credentials.playservices
-                                            .TestCredentialsFragmentActivity.class);
-            activityScenario.onActivity(
-                    activity -> {
-                        listener.onActivity((Activity) activity);
-                    });
-        } else {
-            ActivityScenario<TestCredentialsActivity> activityScenario =
-                    ActivityScenario.launch(TestCredentialsActivity.class);
-            activityScenario.onActivity(
-                    activity -> {
-                        listener.onActivity((Activity) activity);
-                    });
-        }
-    }
-
-    private PublicKeyCredentialCreationOptions convertRequestToPlayServices(
-            Activity activity, String type) {
-        CreatePublicKeyCredentialRequest pubKeyRequest = new CreatePublicKeyCredentialRequest(type);
-        return CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
-                .convertRequestToPlayServices(pubKeyRequest);
-    }
-
     @Test
     public void convertRequestToPlayServices_correctRequiredOnlyRequest_success() {
-        launchTestActivity(
-                activity -> {
-                    try {
-                        JSONObject expectedJson =
-                                new JSONObject(MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+            try {
+                JSONObject expectedJson = new JSONObject(
+                        MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
 
-                        PublicKeyCredentialCreationOptions actualResponse =
-                                convertRequestToPlayServices(
-                                        activity, MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
-                        JSONObject actualJson =
-                                createJsonObjectFromPublicKeyCredentialCreationOptions(
-                                        actualResponse);
-                        JSONObject requiredKeys = new JSONObject(ALL_REQUIRED_FIELDS_SIGNATURE);
+                PublicKeyCredentialCreationOptions actualResponse =
+                        CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
+                                .convertRequestToPlayServices(
+                                        new CreatePublicKeyCredentialRequest(
+                                                MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT));
+                JSONObject actualJson = createJsonObjectFromPublicKeyCredentialCreationOptions(
+                        actualResponse);
+                JSONObject requiredKeys = new JSONObject(ALL_REQUIRED_FIELDS_SIGNATURE);
 
-                        assertThat(
-                                        TestUtils.Companion.isSubsetJson(
-                                                expectedJson, actualJson, requiredKeys))
-                                .isTrue();
-                    } catch (JSONException e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+                assertThat(TestUtils.Companion.isSubsetJson(expectedJson, actualJson,
+                        requiredKeys)).isTrue();
+                // TODO("Add remaining tests in detail after discussing ideal form")
+            } catch (JSONException e) {
+                throw new RuntimeException(e);
+            }
+        });
     }
 
     @Test
     public void convertRequestToPlayServices_correctRequiredAndOptionalRequest_success() {
-        launchTestActivity(
-                activity -> {
-                    try {
-                        JSONObject expectedJson =
-                                new JSONObject(
-                                        MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT);
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+            try {
+                JSONObject expectedJson = new JSONObject(
+                        MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT);
 
-                        PublicKeyCredentialCreationOptions actualResponse =
-                                convertRequestToPlayServices(
-                                        activity,
-                                        MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT);
-                        JSONObject actualJson =
-                                createJsonObjectFromPublicKeyCredentialCreationOptions(
-                                        actualResponse);
-                        JSONObject requiredKeys =
-                                new JSONObject(ALL_REQUIRED_AND_OPTIONAL_SIGNATURE);
+                PublicKeyCredentialCreationOptions actualResponse =
+                        CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
+                                .convertRequestToPlayServices(new CreatePublicKeyCredentialRequest(
+                                        MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT));
+                JSONObject actualJson =
+                        createJsonObjectFromPublicKeyCredentialCreationOptions(
+                                actualResponse);
+                JSONObject requiredKeys = new JSONObject(ALL_REQUIRED_AND_OPTIONAL_SIGNATURE);
 
-                        assertThat(
-                                        TestUtils.Companion.isSubsetJson(
-                                                expectedJson, actualJson, requiredKeys))
-                                .isTrue();
-                    } catch (JSONException e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+                assertThat(TestUtils.Companion.isSubsetJson(expectedJson, actualJson,
+                        requiredKeys)).isTrue();
+                // TODO("Add remaining tests in detail after discussing ideal form")
+            } catch (JSONException e) {
+                throw new RuntimeException(e);
+            }
+        });
     }
-
     @Test
     public void convertRequestToPlayServices_missingRequired_throws() {
-        launchTestActivity(
-                activity -> {
-                    try {
-                        PublicKeyCredentialCreationOptions actualResponse =
-                                convertRequestToPlayServices(
-                                        activity, MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
-
-                        CreatePublicKeyCredentialRequest pubKeyRequest =
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+            try {
+                CredentialProviderCreatePublicKeyCredentialController
+                        .getInstance(activity)
+                        .convertRequestToPlayServices(
                                 new CreatePublicKeyCredentialRequest(
-                                        MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
-                        CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
-                                .convertRequestToPlayServices(
-                                        new CreatePublicKeyCredentialRequest(
-                                                MAIN_CREATE_JSON_MISSING_REQUIRED_FIELD));
+                                        MAIN_CREATE_JSON_MISSING_REQUIRED_FIELD));
 
-                        // Should not reach here.
-                        assertWithMessage("Exception should be thrown").that(true).isFalse();
-                    } catch (Exception e) {
-                        assertThat(e.getMessage().contains("No value for id")).isTrue();
-                        assertThat(e.getClass().getName().contains("JSONException")).isTrue();
-                    }
-                });
+                // Should not reach here.
+                assertWithMessage("Exception should be thrown").that(true).isFalse();
+            } catch (Exception e) {
+                assertThat(e.getMessage().contains("No value for id")).isTrue();
+                assertThat(e.getClass().getName().contains("JSONException")).isTrue();
+            }
+        });
     }
 
     @Test
     public void convertRequestToPlayServices_emptyRequired_throws() {
-        launchTestActivity(
-                activity -> {
-                    assertThrows(
-                            "Expected bad required json to throw",
-                            JSONException.class,
-                            () ->
-                                    convertRequestToPlayServices(
-                                            activity, MAIN_CREATE_JSON_REQUIRED_FIELD_EMPTY));
-                });
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+
+            assertThrows("Expected bad required json to throw",
+                    JSONException.class,
+                    () -> CredentialProviderCreatePublicKeyCredentialController
+                            .getInstance(activity).convertRequestToPlayServices(
+                                    new CreatePublicKeyCredentialRequest(
+                                            MAIN_CREATE_JSON_REQUIRED_FIELD_EMPTY)));
+        });
     }
 
     @Test
     public void convertRequestToPlayServices_missingOptionalRequired_throws() {
-        launchTestActivity(
-                activity -> {
-                    assertThrows(
-                            "Expected bad required json to throw",
-                            JSONException.class,
-                            () ->
-                                    convertRequestToPlayServices(
-                                            activity, OPTIONAL_FIELD_MISSING_REQUIRED_SUBFIELD));
-                });
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+
+            assertThrows("Expected bad required json to throw",
+                    JSONException.class,
+                    () -> CredentialProviderCreatePublicKeyCredentialController
+                            .getInstance(activity)
+                            .convertRequestToPlayServices(
+                                    new CreatePublicKeyCredentialRequest(
+                                            OPTIONAL_FIELD_MISSING_REQUIRED_SUBFIELD)));
+        });
     }
 
     @Test
     public void convertRequestToPlayServices_emptyOptionalRequired_throws() {
-        launchTestActivity(
-                activity -> {
-                    assertThrows(
-                            "Expected bad required json to throw",
-                            JSONException.class,
-                            () ->
-                                    convertRequestToPlayServices(
-                                            activity, OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD));
-                });
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+
+            assertThrows("Expected bad required json to throw",
+                    JSONException.class,
+                    () -> CredentialProviderCreatePublicKeyCredentialController
+                            .getInstance(activity)
+                            .convertRequestToPlayServices(
+                                    new CreatePublicKeyCredentialRequest(
+                                            OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD)));
+        });
     }
 
     @Test
     public void convertRequestToPlayServices_missingOptionalNotRequired_success() {
-        launchTestActivity(
-                activity -> {
-                    try {
-                        JSONObject expectedJson =
-                                new JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD);
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+            try {
+                JSONObject expectedJson = new JSONObject(
+                        OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD);
 
-                        PublicKeyCredentialCreationOptions actualResponse =
-                                convertRequestToPlayServices(
-                                        activity, OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD);
-                        JSONObject actualJson =
-                                createJsonObjectFromPublicKeyCredentialCreationOptions(
-                                        actualResponse);
-                        JSONObject requiredKeys =
-                                new JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD_SIGNATURE);
+                PublicKeyCredentialCreationOptions actualResponse =
+                        CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
+                                .convertRequestToPlayServices(
+                                        new CreatePublicKeyCredentialRequest(
+                                                OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD));
+                JSONObject actualJson = createJsonObjectFromPublicKeyCredentialCreationOptions(
+                        actualResponse);
+                JSONObject requiredKeys = new
+                        JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD_SIGNATURE);
 
-                        assertThat(
-                                        TestUtils.Companion.isSubsetJson(
-                                                expectedJson, actualJson, requiredKeys))
-                                .isTrue();
-                    } catch (JSONException e) {
-                        throw new RuntimeException(e);
-                    }
-                });
-    }
-
-    @Test
-    public void getInstanceRepeatedTest() {
-        launchTestActivity(
-                a -> {
-                    CredentialProviderCreatePublicKeyCredentialController firstInstance =
-                            CredentialProviderCreatePublicKeyCredentialController.getInstance(a);
-                    CredentialProviderCreatePublicKeyCredentialController secondInstance =
-                            CredentialProviderCreatePublicKeyCredentialController.getInstance(a);
-                    assertThat(firstInstance).isEqualTo(secondInstance);
-                });
+                assertThat(TestUtils.Companion.isSubsetJson(expectedJson, actualJson,
+                        requiredKeys)).isTrue();
+                // TODO("Add remaining tests in detail after discussing ideal form")
+            } catch (JSONException e) {
+                throw new RuntimeException(e);
+            }
+        });
     }
 }
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerTest.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerTest.kt
index 0e8a50e..3a9ae60 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerTest.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerTest.kt
@@ -16,9 +16,6 @@
 
 package androidx.credentials.playservices.createpublickeycredential
 
-import android.app.Activity
-import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.credentials.CreatePublicKeyCredentialRequest
 import androidx.credentials.playservices.TestCredentialsActivity
 import androidx.credentials.playservices.TestUtils.Companion.isSubsetJson
@@ -35,6 +32,7 @@
 import androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.Companion.OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD
 import androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.Companion.createJsonObjectFromPublicKeyCredentialCreationOptions
 import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import org.json.JSONException
@@ -43,51 +41,29 @@
 import org.junit.Test
 import org.junit.function.ThrowingRunnable
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
 
-@RunWith(Parameterized::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
-class CredentialProviderCreatePublicKeyCredentialControllerTest(val useFragmentActivity: Boolean) {
-
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "{0}")
-        fun initParameters() = listOf(true, false)
-    }
-
-    @DoNotInline
-    private fun launchTestActivity(callback: (activity: Activity) -> Unit) {
-        if (useFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            var activityScenario =
-                            ActivityScenario.launch(
-                                    androidx.credentials.playservices
-                                            .TestCredentialsFragmentActivity::class.java)
-            activityScenario.onActivity { activity: Activity ->
-                callback.invoke(activity)
-            }
-        } else {
-            var activityScenario = ActivityScenario.launch(TestCredentialsActivity::class.java)
-            activityScenario.onActivity { activity: Activity ->
-                callback.invoke(activity)
-            }
-        }
-    }
-
+class CredentialProviderCreatePublicKeyCredentialControllerTest {
     @Test
     fun convertRequestToPlayServices_correctRequiredOnlyRequest_success() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
             try {
                 val expectedJson = JSONObject(MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT)
 
-                val actualResponse = getInstance(activity).convertRequestToPlayServices(
-                            CreatePublicKeyCredentialRequest(
-                                MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT))
+                val actualResponse = getInstance(activity!!).convertRequestToPlayServices(
+                    CreatePublicKeyCredentialRequest(
+                        MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT))
                 val actualJson =
                     createJsonObjectFromPublicKeyCredentialCreationOptions(actualResponse)
                 val requiredKeys =
                     JSONObject(ALL_REQUIRED_FIELDS_SIGNATURE)
 
                 assertThat(isSubsetJson(expectedJson, actualJson, requiredKeys)).isTrue()
+                // TODO("Add remaining tests in detail after discussing ideal form")
             } catch (e: JSONException) {
                 throw RuntimeException(e)
             }
@@ -96,20 +72,24 @@
 
     @Test
     fun convertRequestToPlayServices_correctRequiredAndOptionalRequest_success() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
             try {
                 val expectedJson = JSONObject(
                     MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT)
 
-                val actualResponse = getInstance(activity)
-                        .convertRequestToPlayServices(CreatePublicKeyCredentialRequest(
-                            MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT))
+                val actualResponse = getInstance(activity!!)
+                    .convertRequestToPlayServices(CreatePublicKeyCredentialRequest(
+                        MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT))
                 val actualJson =
                     createJsonObjectFromPublicKeyCredentialCreationOptions(actualResponse)
                 val requiredKeys =
                     JSONObject(ALL_REQUIRED_AND_OPTIONAL_SIGNATURE)
 
                 assertThat(isSubsetJson(expectedJson, actualJson, requiredKeys)).isTrue()
+                // TODO("Add remaining tests in detail after discussing ideal form")
             } catch (e: JSONException) {
                 throw java.lang.RuntimeException(e)
             }
@@ -118,88 +98,94 @@
 
     @Test
     fun convertRequestToPlayServices_missingRequired_throws() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
             Assert.assertThrows("Expected bad required json to throw",
                 JSONException::class.java,
                 ThrowingRunnable {
                     getInstance(
-                        activity
+                        activity!!
                     ).convertRequestToPlayServices(
-                            CreatePublicKeyCredentialRequest(
-                                MAIN_CREATE_JSON_MISSING_REQUIRED_FIELD
-                            )) })
+                        CreatePublicKeyCredentialRequest(
+                            MAIN_CREATE_JSON_MISSING_REQUIRED_FIELD
+                        )) })
         }
     }
 
     @Test
     fun convertRequestToPlayServices_emptyRequired_throws() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
             Assert.assertThrows("Expected bad required json to throw",
                 JSONException::class.java,
-                ThrowingRunnable { getInstance(activity
-                    ).convertRequestToPlayServices(CreatePublicKeyCredentialRequest(
-                                MAIN_CREATE_JSON_REQUIRED_FIELD_EMPTY)) })
+                ThrowingRunnable { getInstance(activity!!
+                ).convertRequestToPlayServices(CreatePublicKeyCredentialRequest(
+                    MAIN_CREATE_JSON_REQUIRED_FIELD_EMPTY)) })
         }
     }
     @Test
     fun convertRequestToPlayServices_missingOptionalRequired_throws() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
             Assert.assertThrows("Expected bad required json to throw",
                 JSONException::class.java,
                 ThrowingRunnable {
                     getInstance(
-                        activity
+                        activity!!
                     ).convertRequestToPlayServices(
-                            CreatePublicKeyCredentialRequest(
-                                OPTIONAL_FIELD_MISSING_REQUIRED_SUBFIELD)) })
+                        CreatePublicKeyCredentialRequest(
+                            OPTIONAL_FIELD_MISSING_REQUIRED_SUBFIELD)) })
         }
     }
 
     @Test
     fun convertRequestToPlayServices_emptyOptionalRequired_throws() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
             Assert.assertThrows("Expected bad required json to throw",
                 JSONException::class.java,
-                ThrowingRunnable { getInstance(activity).convertRequestToPlayServices(
-                            CreatePublicKeyCredentialRequest(
-                                OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD)) })
+                ThrowingRunnable { getInstance(activity!!).convertRequestToPlayServices(
+                    CreatePublicKeyCredentialRequest(
+                        OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD)) })
         }
     }
 
     @Test
     fun convertRequestToPlayServices_missingOptionalNotRequired_success() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
             try {
                 val expectedJson = JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD)
 
                 val actualResponse =
-                    getInstance(activity)
+                    getInstance(activity!!)
                         .convertRequestToPlayServices(
                             CreatePublicKeyCredentialRequest(
                                 OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD))
                 val actualJson = createJsonObjectFromPublicKeyCredentialCreationOptions(
-                        actualResponse)
+                    actualResponse)
                 val requiredKeys =
                     JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD_SIGNATURE)
 
                 assertThat(isSubsetJson(expectedJson, actualJson, requiredKeys)).isTrue()
+                // TODO("Add remaining tests in detail after discussing ideal form")
             } catch (e: JSONException) {
                 throw java.lang.RuntimeException(e)
             }
         }
     }
-
-    @Test
-    fun getInstanceRepeatedTest() {
-        launchTestActivity { activity: Activity ->
-
-            val firstInstance = getInstance(activity)
-            val secondInstance = getInstance(activity)
-            assertThat(firstInstance).isEqualTo(secondInstance)
-        }
-    }
 }
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderFragment.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderFragment.kt
deleted file mode 100644
index 2d7f1e9..0000000
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderFragment.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2023 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.credentials.playservices
-
-import android.content.Intent
-import android.os.Bundle
-import android.os.ResultReceiver
-import androidx.annotation.RestrictTo
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController
-import androidx.fragment.app.Fragment
-
-/** A fragment used if we are passed a fragment activity. */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-@Suppress("Deprecation")
-open class CredentialProviderFragment : Fragment() {
-
-  private var resultReceiver: ResultReceiver? = null
-  private var mWaitingForActivityResult = false
-
-  override fun onCreate(savedInstanceState: Bundle?) {
-    super.onCreate(savedInstanceState)
-
-    resultReceiver = getResultReceiver()
-    if (resultReceiver == null) {
-      return
-    }
-
-    restoreState(savedInstanceState)
-    if (mWaitingForActivityResult) {
-      return // Past call still active
-    }
-  }
-
-  private fun getResultReceiver(): ResultReceiver? {
-    if (getArguments() == null) {
-      return null
-    }
-
-    return getArguments()!!.getParcelable(CredentialProviderBaseController.RESULT_RECEIVER_TAG)
-      as? ResultReceiver
-  }
-
-  private fun restoreState(savedInstanceState: Bundle?) {
-    if (savedInstanceState != null) {
-      mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false)
-    }
-  }
-
-  override fun onSaveInstanceState(outState: Bundle) {
-    outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult)
-    super.onSaveInstanceState(outState)
-  }
-
-  override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
-    super.onActivityResult(requestCode, resultCode, data)
-    val bundle = Bundle()
-    bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, false)
-    bundle.putInt(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, requestCode)
-    bundle.putParcelable(CredentialProviderBaseController.RESULT_DATA_TAG, data)
-    resultReceiver?.send(resultCode, bundle)
-    mWaitingForActivityResult = false
-  }
-
-  companion object {
-    private const val TAG = "CredentialProviderFragment"
-    private const val KEY_AWAITING_RESULT = "androidx.credentials.playservices.AWAITING_RESULT"
-
-    fun createFrom(resultReceiver: ResultReceiver): CredentialProviderFragment {
-      val f = CredentialProviderFragment()
-
-      // Supply index input as an argument.
-      val args = Bundle()
-      args.putParcelable(CredentialProviderBaseController.RESULT_RECEIVER_TAG, resultReceiver)
-      f.setArguments(args)
-
-      return f
-    }
-  }
-}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/GmsCoreUtils.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/GmsCoreUtils.kt
deleted file mode 100644
index 112599d..0000000
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/GmsCoreUtils.kt
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * 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.credentials.playservices
-
-import android.app.Activity
-import android.app.PendingIntent
-import android.content.IntentSender
-import android.os.Bundle
-import android.os.ResultReceiver
-import android.util.Log
-import androidx.annotation.RestrictTo
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.CREATE_INTERRUPTED
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.CREATE_UNKNOWN
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_INTERRUPTED
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_NO_CREDENTIALS
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_UNKNOWN
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentActivity
-import com.google.android.gms.auth.api.identity.BeginSignInRequest
-import com.google.android.gms.auth.api.identity.CredentialSavingClient
-import com.google.android.gms.auth.api.identity.SavePasswordRequest
-import com.google.android.gms.auth.api.identity.SignInClient
-import com.google.android.gms.common.api.ApiException
-import com.google.android.gms.fido.fido2.Fido2ApiClient
-import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions
-
-/** A util class for interacting with GmsCore. */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-@Suppress("Deprecation", "ForbiddenSuperClass")
-open class GmsCoreUtils {
-
-  class GmsCoreUtilsResult(var waitingForActivityResult: Boolean, var hasFinished: Boolean)
-
-  internal companion object {
-    private const val TAG = "GmsCoreUtils"
-
-    const val DEFAULT_REQUEST_CODE = 1
-
-    class FragmentCreationException() : Exception("Failed to create exception")
-
-    fun handleCreatePublicKeyCredential(
-      apiClient: Fido2ApiClient,
-      resultReceiver: ResultReceiver,
-      fidoRegistrationRequest: PublicKeyCredentialCreationOptions?,
-      requestCode: Int,
-      activity: Activity
-    ): GmsCoreUtilsResult {
-      var waitingForActivityResult = false
-      var hasActivityFinished = false
-      var fragment = setupFragmentActivity(activity, resultReceiver)
-
-      fidoRegistrationRequest?.let {
-        apiClient
-          .getRegisterPendingIntent(fidoRegistrationRequest)
-          .addOnSuccessListener { result: PendingIntent ->
-            try {
-              startIntentSender(
-                activity,
-                result.intentSender,
-                requestCode,
-                fragment,
-              )
-            } catch (e: IntentSender.SendIntentException) {
-              setupFailure(
-                resultReceiver,
-                CREATE_UNKNOWN,
-                "During public key credential, found IntentSender " +
-                  "failure on public key creation: ${e.message}"
-              )
-            }
-          }
-          .addOnFailureListener { e: Exception ->
-            var errName: String = CREATE_UNKNOWN
-            if (e is ApiException && e.statusCode in CredentialProviderBaseController.retryables) {
-              errName = CREATE_INTERRUPTED
-            }
-            setupFailure(
-              resultReceiver,
-              errName,
-              "During create public key credential, fido registration " + "failure: ${e.message}"
-            )
-          }
-      }
-        ?: run {
-          Log.w(
-            TAG,
-            "During create public key credential, request is null, so nothing to " +
-              "launch for public key credentials"
-          )
-          hasActivityFinished = true
-        }
-      return GmsCoreUtilsResult(waitingForActivityResult, hasActivityFinished)
-    }
-
-    fun handleBeginSignIn(
-      apiClient: SignInClient,
-      resultReceiver: ResultReceiver,
-      params: BeginSignInRequest?,
-      requestCode: Int,
-      activity: Activity
-    ): GmsCoreUtilsResult {
-      var waitingForActivityResult = false
-      var hasFinished = false
-      var fragment = setupFragmentActivity(activity, resultReceiver)
-
-      params?.let {
-        apiClient
-          .beginSignIn(params)
-          .addOnSuccessListener {
-            try {
-              waitingForActivityResult = true
-              startIntentSender(
-                activity,
-                it.pendingIntent.intentSender,
-                requestCode,
-                fragment,
-              )
-            } catch (e: IntentSender.SendIntentException) {
-              setupFailure(
-                resultReceiver,
-                GET_UNKNOWN,
-                "During begin sign in, one tap ui intent sender " + "failure: ${e.message}"
-              )
-            }
-          }
-          .addOnFailureListener { e: Exception ->
-            var errName: String = GET_NO_CREDENTIALS
-            if (e is ApiException && e.statusCode in CredentialProviderBaseController.retryables) {
-              errName = GET_INTERRUPTED
-            }
-            setupFailure(
-              resultReceiver,
-              errName,
-              "During begin sign in, failure response from one tap: ${e.message}"
-            )
-          }
-      }
-        ?: run {
-          Log.i(
-            TAG,
-            "During begin sign in, params is null, nothing to launch for " + "begin sign in"
-          )
-          hasFinished = true
-        }
-      return GmsCoreUtilsResult(waitingForActivityResult, hasFinished)
-    }
-
-    fun handleCreatePassword(
-      apiClient: CredentialSavingClient,
-      resultReceiver: ResultReceiver,
-      params: SavePasswordRequest?,
-      requestCode: Int,
-      activity: Activity
-    ): GmsCoreUtilsResult {
-      var waitingForActivityResult = false
-      var hasFinished = false
-      var fragment = setupFragmentActivity(activity, resultReceiver)
-
-      params?.let {
-        apiClient
-          .savePassword(params)
-          .addOnSuccessListener {
-            try {
-              waitingForActivityResult = true
-              startIntentSender(
-                activity,
-                it.pendingIntent.intentSender,
-                requestCode,
-                fragment,
-              )
-            } catch (e: IntentSender.SendIntentException) {
-              setupFailure(
-                resultReceiver,
-                CREATE_UNKNOWN,
-                "During save password, found UI intent sender " + "failure: ${e.message}"
-              )
-            }
-          }
-          .addOnFailureListener { e: Exception ->
-            var errName: String = CREATE_UNKNOWN
-            if (e is ApiException && e.statusCode in CredentialProviderBaseController.retryables) {
-              errName = CREATE_INTERRUPTED
-            }
-            setupFailure(
-              resultReceiver,
-              errName,
-              "During save password, found " + "password failure response from one tap ${e.message}"
-            )
-          }
-      }
-        ?: run {
-          Log.i(
-            TAG,
-            "During save password, params is null, nothing to launch for create" + " password"
-          )
-          hasFinished = true
-        }
-      return GmsCoreUtilsResult(waitingForActivityResult, hasFinished)
-    }
-
-    private fun setupFragmentActivity(
-      activity: Activity,
-      resultReceiver: ResultReceiver
-    ): Fragment? {
-      if (activity is FragmentActivity) {
-        val fragment = CredentialProviderFragment.createFrom(resultReceiver)
-        val manager = activity.getSupportFragmentManager()
-        manager.beginTransaction().add(fragment, "credman").commit()
-
-        if (!fragment.isAdded()) {
-          throw FragmentCreationException()
-        }
-
-        return fragment
-      }
-
-      return null
-    }
-
-    private fun startIntentSender(
-      activity: Activity,
-      intentSender: IntentSender,
-      requestCode: Int,
-      fragment: Fragment?,
-    ) {
-      if (fragment != null && fragment.isAdded() && activity is FragmentActivity) {
-        activity.startIntentSenderFromFragment(
-          fragment,
-          intentSender,
-          requestCode,
-          null,
-          0,
-          0,
-          0,
-          null,
-        )
-        return
-      }
-
-      activity.startIntentSenderForResult(intentSender, requestCode, null, 0, 0, 0, null)
-    }
-
-    private fun setupFailure(resultReceiver: ResultReceiver, errName: String, errMsg: String) {
-      val bundle = Bundle()
-      bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, true)
-      bundle.putString(CredentialProviderBaseController.EXCEPTION_TYPE_TAG, errName)
-      bundle.putString(CredentialProviderBaseController.EXCEPTION_MESSAGE_TAG, errMsg)
-      resultReceiver.send(Integer.MAX_VALUE, bundle)
-    }
-  }
-}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/HiddenActivity.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/HiddenActivity.kt
index 8586305..bde5471 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/HiddenActivity.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/HiddenActivity.kt
@@ -17,150 +17,230 @@
 package androidx.credentials.playservices
 
 import android.app.Activity
+import android.app.PendingIntent
 import android.content.Intent
+import android.content.IntentSender
 import android.os.Bundle
 import android.os.ResultReceiver
 import android.util.Log
 import androidx.annotation.RestrictTo
 import androidx.credentials.playservices.controllers.CredentialProviderBaseController
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.CREATE_INTERRUPTED
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.CREATE_UNKNOWN
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_INTERRUPTED
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_NO_CREDENTIALS
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_UNKNOWN
 import com.google.android.gms.auth.api.identity.BeginSignInRequest
 import com.google.android.gms.auth.api.identity.Identity
 import com.google.android.gms.auth.api.identity.SavePasswordRequest
+import com.google.android.gms.common.api.ApiException
 import com.google.android.gms.fido.Fido
 import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions
 
-/** An activity used to ensure all required API versions work as intended. */
+/**
+ * An activity used to ensure all required API versions work as intended.
+ */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 @Suppress("Deprecation", "ForbiddenSuperClass")
 open class HiddenActivity : Activity() {
 
-  private var resultReceiver: ResultReceiver? = null
-  private var mWaitingForActivityResult = false
+    private var resultReceiver: ResultReceiver? = null
+    private var mWaitingForActivityResult = false
 
-  override fun onCreate(savedInstanceState: Bundle?) {
-    super.onCreate(savedInstanceState)
-    overridePendingTransition(0, 0)
-    val type: String? = intent.getStringExtra(CredentialProviderBaseController.TYPE_TAG)
-    resultReceiver = intent.getParcelableExtra(CredentialProviderBaseController.RESULT_RECEIVER_TAG)
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        overridePendingTransition(0, 0)
+        val type: String? = intent.getStringExtra(CredentialProviderBaseController.TYPE_TAG)
+        resultReceiver = intent.getParcelableExtra(
+            CredentialProviderBaseController.RESULT_RECEIVER_TAG)
 
-    if (resultReceiver == null) {
-      finish()
+        if (resultReceiver == null) {
+            finish()
+        }
+
+        restoreState(savedInstanceState)
+        if (mWaitingForActivityResult) {
+            return; // Past call still active
+        }
+
+        when (type) {
+            CredentialProviderBaseController.BEGIN_SIGN_IN_TAG -> {
+                handleBeginSignIn()
+            }
+            CredentialProviderBaseController.CREATE_PASSWORD_TAG -> {
+                handleCreatePassword()
+            }
+            CredentialProviderBaseController.CREATE_PUBLIC_KEY_CREDENTIAL_TAG -> {
+                handleCreatePublicKeyCredential()
+            } else -> {
+                Log.w(TAG, "Activity handed an unsupported type")
+                finish()
+            }
+        }
     }
 
-    restoreState(savedInstanceState)
-    if (mWaitingForActivityResult) {
-      return
-      // Past call still active
+    private fun restoreState(savedInstanceState: Bundle?) {
+        if (savedInstanceState != null) {
+            mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false)
+        }
     }
 
-    when (type) {
-      CredentialProviderBaseController.BEGIN_SIGN_IN_TAG -> {
-        handleBeginSignIn(intent, resultReceiver)
-      }
-      CredentialProviderBaseController.CREATE_PASSWORD_TAG -> {
-        handleCreatePassword(intent, resultReceiver)
-      }
-      CredentialProviderBaseController.CREATE_PUBLIC_KEY_CREDENTIAL_TAG -> {
-        handleCreatePublicKeyCredential(intent, resultReceiver)
-      }
-      else -> {
-        Log.w(TAG, "Activity handed an unsupported type")
+    private fun handleCreatePublicKeyCredential() {
+        val fidoRegistrationRequest: PublicKeyCredentialCreationOptions? = intent
+            .getParcelableExtra(CredentialProviderBaseController.REQUEST_TAG)
+        val requestCode: Int = intent.getIntExtra(
+            CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG,
+                DEFAULT_VALUE)
+        fidoRegistrationRequest?.let {
+            Fido.getFido2ApiClient(this)
+                .getRegisterPendingIntent(fidoRegistrationRequest)
+                .addOnSuccessListener { result: PendingIntent ->
+                    try {
+                        mWaitingForActivityResult = true
+                        startIntentSenderForResult(
+                            result.intentSender,
+                            requestCode,
+                            null, /* fillInIntent= */
+                            0, /* flagsMask= */
+                            0, /* flagsValue= */
+                            0, /* extraFlags= */
+                            null /* options= */
+                        )
+                    } catch (e: IntentSender.SendIntentException) {
+                        setupFailure(resultReceiver!!,
+                            CREATE_UNKNOWN,
+                            "During public key credential, found IntentSender " +
+                                "failure on public key creation: ${e.message}")
+                    }
+                }
+                .addOnFailureListener { e: Exception ->
+                    var errName: String = CREATE_UNKNOWN
+                    if (e is ApiException && e.statusCode in
+                        CredentialProviderBaseController.retryables) {
+                        errName = CREATE_INTERRUPTED
+                    }
+                    setupFailure(resultReceiver!!, errName,
+                        "During create public key credential, fido registration " +
+                            "failure: ${e.message}")
+                }
+        } ?: run {
+            Log.w(TAG, "During create public key credential, request is null, so nothing to " +
+                "launch for public key credentials")
+            finish()
+        }
+    }
+
+    private fun setupFailure(resultReceiver: ResultReceiver, errName: String, errMsg: String) {
+        val bundle = Bundle()
+        bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, true)
+        bundle.putString(CredentialProviderBaseController.EXCEPTION_TYPE_TAG, errName)
+        bundle.putString(CredentialProviderBaseController.EXCEPTION_MESSAGE_TAG, errMsg)
+        resultReceiver.send(Integer.MAX_VALUE, bundle)
         finish()
-      }
     }
-  }
 
-  private fun restoreState(savedInstanceState: Bundle?) {
-    if (savedInstanceState != null) {
-      mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false)
+    override fun onSaveInstanceState(outState: Bundle) {
+        outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult)
+        super.onSaveInstanceState(outState)
     }
-  }
 
-  private fun handleBeginSignIn(intent: Intent, resultReceiver: ResultReceiver?) {
-    val params: BeginSignInRequest? =
-      intent.getParcelableExtra(CredentialProviderBaseController.REQUEST_TAG)
-    val requestCode: Int =
-      intent.getIntExtra(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, DEFAULT_VALUE)
+    private fun handleBeginSignIn() {
+        val params: BeginSignInRequest? = intent.getParcelableExtra(
+            CredentialProviderBaseController.REQUEST_TAG)
+        val requestCode: Int = intent.getIntExtra(
+            CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG,
+            DEFAULT_VALUE)
+        params?.let {
+            Identity.getSignInClient(this).beginSignIn(params).addOnSuccessListener {
+                try {
+                    mWaitingForActivityResult = true
+                    startIntentSenderForResult(
+                        it.pendingIntent.intentSender,
+                        requestCode,
+                        null,
+                        0,
+                        0,
+                        0,
+                        null
+                    )
+                } catch (e: IntentSender.SendIntentException) {
+                    setupFailure(resultReceiver!!,
+                        GET_UNKNOWN,
+                            "During begin sign in, one tap ui intent sender " +
+                                "failure: ${e.message}")
+                }
+            }.addOnFailureListener { e: Exception ->
+                var errName: String = GET_NO_CREDENTIALS
+                if (e is ApiException && e.statusCode in
+                    CredentialProviderBaseController.retryables) {
+                    errName = GET_INTERRUPTED
+                }
+                setupFailure(resultReceiver!!, errName,
+                    "During begin sign in, failure response from one tap: ${e.message}")
+            }
+        } ?: run {
+            Log.i(TAG, "During begin sign in, params is null, nothing to launch for " +
+                "begin sign in")
+            finish()
+        }
+    }
 
-    if (intent.hasExtra(CredentialProviderBaseController.REQUEST_TAG) && resultReceiver != null) {
-      val result =
-        GmsCoreUtils.handleBeginSignIn(
-          Identity.getSignInClient(this),
-          resultReceiver,
-          params!!,
-          requestCode,
-          this
-        )
-      mWaitingForActivityResult = result.waitingForActivityResult
-      if (result.hasFinished) {
+    private fun handleCreatePassword() {
+        val params: SavePasswordRequest? = intent.getParcelableExtra(
+            CredentialProviderBaseController.REQUEST_TAG)
+        val requestCode: Int = intent.getIntExtra(
+            CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG,
+            DEFAULT_VALUE)
+        params?.let {
+            Identity.getCredentialSavingClient(this).savePassword(params)
+                .addOnSuccessListener {
+                    try {
+                        mWaitingForActivityResult = true
+                        startIntentSenderForResult(
+                            it.pendingIntent.intentSender,
+                            requestCode,
+                            null,
+                            0,
+                            0,
+                            0,
+                            null
+                        )
+                    } catch (e: IntentSender.SendIntentException) {
+                        setupFailure(resultReceiver!!,
+                            CREATE_UNKNOWN,
+                                "During save password, found UI intent sender " +
+                                    "failure: ${e.message}")
+                    }
+            }.addOnFailureListener { e: Exception ->
+                    var errName: String = CREATE_UNKNOWN
+                    if (e is ApiException && e.statusCode in
+                        CredentialProviderBaseController.retryables) {
+                        errName = CREATE_INTERRUPTED
+                    }
+                    setupFailure(resultReceiver!!, errName, "During save password, found " +
+                        "password failure response from one tap ${e.message}")
+            }
+        } ?: run {
+            Log.i(TAG, "During save password, params is null, nothing to launch for create" +
+                " password")
+            finish()
+        }
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        val bundle = Bundle()
+        bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, false)
+        bundle.putInt(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, requestCode)
+        bundle.putParcelable(CredentialProviderBaseController.RESULT_DATA_TAG, data)
+        resultReceiver?.send(resultCode, bundle)
+        mWaitingForActivityResult = false
         finish()
-      }
     }
-  }
 
-  private fun handleCreatePassword(intent: Intent, resultReceiver: ResultReceiver?) {
-    val params: SavePasswordRequest? =
-      intent.getParcelableExtra(CredentialProviderBaseController.REQUEST_TAG)
-    val requestCode: Int =
-      intent.getIntExtra(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, DEFAULT_VALUE)
-
-    if (intent.hasExtra(CredentialProviderBaseController.REQUEST_TAG) && resultReceiver != null) {
-      val result =
-        GmsCoreUtils.handleCreatePassword(
-          Identity.getCredentialSavingClient(this),
-          resultReceiver,
-          params!!,
-          requestCode,
-          this
-        )
-      mWaitingForActivityResult = result.waitingForActivityResult
-      if (result.hasFinished) {
-        finish()
-      }
+    companion object {
+        private const val DEFAULT_VALUE: Int = 1
+        private const val TAG = "HiddenActivity"
+        private const val KEY_AWAITING_RESULT = "androidx.credentials.playservices.AWAITING_RESULT"
     }
-  }
-
-  private fun handleCreatePublicKeyCredential(intent: Intent, resultReceiver: ResultReceiver?) {
-    val fidoRegistrationRequest: PublicKeyCredentialCreationOptions? =
-      intent.getParcelableExtra(CredentialProviderBaseController.REQUEST_TAG)
-    val requestCode: Int =
-      intent.getIntExtra(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, DEFAULT_VALUE)
-
-    if (intent.hasExtra(CredentialProviderBaseController.REQUEST_TAG) && resultReceiver != null) {
-      val result =
-        GmsCoreUtils.handleCreatePublicKeyCredential(
-          Fido.getFido2ApiClient(this),
-          resultReceiver,
-          fidoRegistrationRequest!!,
-          requestCode,
-          this
-        )
-      mWaitingForActivityResult = result.waitingForActivityResult
-      if (result.hasFinished) {
-        finish()
-      }
-    }
-  }
-
-  override fun onSaveInstanceState(outState: Bundle) {
-    outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult)
-    super.onSaveInstanceState(outState)
-  }
-
-  override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
-    super.onActivityResult(requestCode, resultCode, data)
-    val bundle = Bundle()
-    bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, false)
-    bundle.putInt(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, requestCode)
-    bundle.putParcelable(CredentialProviderBaseController.RESULT_DATA_TAG, data)
-    resultReceiver?.send(resultCode, bundle)
-    mWaitingForActivityResult = false
-    finish()
-  }
-
-  companion object {
-    private const val DEFAULT_VALUE: Int = 1
-    private const val TAG = "HiddenActivity"
-    private const val KEY_AWAITING_RESULT = "androidx.credentials.playservices.AWAITING_RESULT"
-  }
 }
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
index 18f8a16..b0d9453 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
@@ -36,13 +36,11 @@
 import androidx.credentials.exceptions.GetCredentialInterruptedException
 import androidx.credentials.exceptions.GetCredentialUnknownException
 import androidx.credentials.playservices.CredentialProviderPlayServicesImpl
-import androidx.credentials.playservices.GmsCoreUtils
 import androidx.credentials.playservices.HiddenActivity
 import androidx.credentials.playservices.controllers.BeginSignIn.BeginSignInControllerUtility.Companion.constructBeginSignInRequest
 import androidx.credentials.playservices.controllers.CreatePublicKeyCredential.PublicKeyCredentialControllerUtility
 import androidx.credentials.playservices.controllers.CredentialProviderBaseController
 import androidx.credentials.playservices.controllers.CredentialProviderController
-import androidx.fragment.app.FragmentActivity
 import com.google.android.gms.auth.api.identity.BeginSignInRequest
 import com.google.android.gms.auth.api.identity.Identity
 import com.google.android.gms.auth.api.identity.SignInCredential
@@ -119,20 +117,6 @@
         }
 
         val convertedRequest: BeginSignInRequest = this.convertRequestToPlayServices(request)
-
-        // If we were passed a fragment activity use that instead of a hidden one.
-        if (context is FragmentActivity) {
-            try {
-                GmsCoreUtils.handleBeginSignIn(
-                    Identity.getSignInClient(context),
-                    resultReceiver, convertedRequest, GmsCoreUtils.DEFAULT_REQUEST_CODE,
-                    context)
-                return
-            } catch (e: Exception) {
-                Log.e(TAG, "Failed to use fragment flow", e)
-            }
-        }
-
         val hiddenIntent = Intent(context, HiddenActivity::class.java)
         hiddenIntent.putExtra(REQUEST_TAG, convertedRequest)
         generateHiddenActivityIntent(resultReceiver, hiddenIntent, BEGIN_SIGN_IN_TAG)
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
index ee4c3bb..360b0c6 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
@@ -32,12 +32,9 @@
 import androidx.credentials.exceptions.CreateCredentialException
 import androidx.credentials.exceptions.CreateCredentialUnknownException
 import androidx.credentials.playservices.CredentialProviderPlayServicesImpl
-import androidx.credentials.playservices.GmsCoreUtils
 import androidx.credentials.playservices.HiddenActivity
 import androidx.credentials.playservices.controllers.CredentialProviderBaseController
 import androidx.credentials.playservices.controllers.CredentialProviderController
-import androidx.fragment.app.FragmentActivity
-import com.google.android.gms.auth.api.identity.Identity
 import com.google.android.gms.auth.api.identity.SavePasswordRequest
 import com.google.android.gms.auth.api.identity.SignInPassword
 import java.util.concurrent.Executor
@@ -103,20 +100,6 @@
         }
 
         val convertedRequest: SavePasswordRequest = this.convertRequestToPlayServices(request)
-
-        // If we were passed a fragment activity use that instead of a hidden one.
-        if (context is FragmentActivity) {
-            try {
-                GmsCoreUtils.handleCreatePassword(
-                    Identity.getCredentialSavingClient(context),
-                    resultReceiver, convertedRequest, GmsCoreUtils.DEFAULT_REQUEST_CODE,
-                    context)
-                return
-            } catch (e: Exception) {
-                Log.e(TAG, "Failed to use fragment flow", e)
-            }
-        }
-
         val hiddenIntent = Intent(context, HiddenActivity::class.java)
         hiddenIntent.putExtra(REQUEST_TAG, convertedRequest)
         generateHiddenActivityIntent(resultReceiver, hiddenIntent, CREATE_PASSWORD_TAG)
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
index 17a5b34..1302a81 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
@@ -35,11 +35,9 @@
 import androidx.credentials.exceptions.domerrors.UnknownError
 import androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialDomException
 import androidx.credentials.playservices.CredentialProviderPlayServicesImpl
-import androidx.credentials.playservices.GmsCoreUtils
 import androidx.credentials.playservices.HiddenActivity
 import androidx.credentials.playservices.controllers.CredentialProviderBaseController
 import androidx.credentials.playservices.controllers.CredentialProviderController
-import androidx.fragment.app.FragmentActivity
 import com.google.android.gms.fido.Fido
 import com.google.android.gms.fido.fido2.api.common.PublicKeyCredential
 import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions
@@ -121,19 +119,6 @@
         if (CredentialProviderPlayServicesImpl.cancellationReviewer(cancellationSignal)) {
             return
         }
-
-        // If we were passed a fragment activity use that instead of a hidden one.
-        if (context is FragmentActivity) {
-            try {
-                GmsCoreUtils.handleCreatePublicKeyCredential(Fido.getFido2ApiClient(context),
-                    resultReceiver, fidoRegistrationRequest, GmsCoreUtils.DEFAULT_REQUEST_CODE,
-                    context)
-                return
-            } catch (e: Exception) {
-                Log.e(TAG, "Failed to use fragment flow", e)
-            }
-        }
-
         val hiddenIntent = Intent(context, HiddenActivity::class.java)
         hiddenIntent.putExtra(REQUEST_TAG, fidoRegistrationRequest)
         generateHiddenActivityIntent(resultReceiver, hiddenIntent,
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceJavaTest.java
index a2c44d0..e8d55f0 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceJavaTest.java
@@ -23,16 +23,22 @@
 import android.os.CancellationSignal;
 import android.os.OutcomeReceiver;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
+import java.util.List;
 
-//@RunWith(AndroidJUnit4.class)
-//@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SmallTest
 @SdkSuppress(minSdkVersion = 34)
 public class CredentialProviderServiceJavaTest {
 
-    //@Test
+    @Test
     public void test_createRequest() {
         CredentialProviderServiceTestImpl service = new CredentialProviderServiceTestImpl();
         service.setTestMode(true);
@@ -60,54 +66,59 @@
         assertThat(service.getLastCreateRequest()).isNotNull();
     }
 
-    //@Test
+    @Test
     public void test_getRequest() {
         CredentialProviderServiceTestImpl service = new CredentialProviderServiceTestImpl();
         service.setTestMode(true);
 
-        BeginGetCredentialRequest request =
-                new BeginGetCredentialRequest(new ArrayList<BeginGetCredentialOption>());
-        OutcomeReceiver<
-                        androidx.credentials.provider.BeginGetCredentialResponse,
-                        androidx.credentials.exceptions.GetCredentialException>
-                outcome =
-                        new OutcomeReceiver<
-                                androidx.credentials.provider.BeginGetCredentialResponse,
-                                androidx.credentials.exceptions.GetCredentialException>() {
-                    public void onResult(
-                                    androidx.credentials.provider.BeginGetCredentialResponse
-                                            response) {}
+        android.service.credentials.BeginGetCredentialOption option =
+                new android.service.credentials.BeginGetCredentialOption(
+                        "id", "type", new Bundle());
+        List<android.service.credentials.BeginGetCredentialOption> options = new ArrayList<>();
+        options.add(option);
 
-                    public void onError(
-                                    androidx.credentials.exceptions.GetCredentialException error) {}
+        android.service.credentials.BeginGetCredentialRequest request =
+                        new android.service.credentials.BeginGetCredentialRequest.Builder()
+                .setBeginGetCredentialOptions(options).build();
+        OutcomeReceiver<
+                        android.service.credentials.BeginGetCredentialResponse,
+                        android.credentials.GetCredentialException>
+                outcome = new OutcomeReceiver<
+                        android.service.credentials.BeginGetCredentialResponse,
+                                android.credentials.GetCredentialException>() {
+                        public void onResult(
+                                android.service.credentials.BeginGetCredentialResponse response) {}
+
+                        public void onError(android.credentials.GetCredentialException error) {}
                 };
 
         // Call the service.
         assertThat(service.getLastGetRequest()).isNull();
-        service.onBeginGetCredentialRequest(request, new CancellationSignal(), outcome);
+        service.onBeginGetCredential(request, new CancellationSignal(), outcome);
         assertThat(service.getLastGetRequest()).isNotNull();
     }
 
-    //@Test
+    @Test
     public void test_clearRequest() {
         CredentialProviderServiceTestImpl service = new CredentialProviderServiceTestImpl();
         service.setTestMode(true);
 
-        ProviderClearCredentialStateRequest request =
-                new ProviderClearCredentialStateRequest(
-                        new CallingAppInfo("name", new SigningInfo()));
-        OutcomeReceiver<Void, androidx.credentials.exceptions.ClearCredentialException> outcome =
+        android.service.credentials.ClearCredentialStateRequest request =
+                new android.service.credentials.ClearCredentialStateRequest(
+                        new android.service.credentials.CallingAppInfo(
+                                "name", new SigningInfo()), new Bundle());
+        OutcomeReceiver<Void, android.credentials.ClearCredentialStateException> outcome =
                 new OutcomeReceiver<
-                        Void, androidx.credentials.exceptions.ClearCredentialException>() {
+                        Void, android.credentials.ClearCredentialStateException>() {
                     public void onResult(Void response) {}
 
                     public void onError(
-                            androidx.credentials.exceptions.ClearCredentialException error) {}
+                            android.credentials.ClearCredentialStateException error) {}
                 };
 
         // Call the service.
         assertThat(service.getLastClearRequest()).isNull();
-        service.onClearCredentialStateRequest(request, new CancellationSignal(), outcome);
+        service.onClearCredentialState(request, new CancellationSignal(), outcome);
         assertThat(service.getLastClearRequest()).isNotNull();
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceTest.kt
index 700c7bd..df32b01 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceTest.kt
@@ -21,17 +21,21 @@
 import android.os.CancellationSignal
 import android.os.OutcomeReceiver
 import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
 
 @SdkSuppress(minSdkVersion = 34)
-// @RunWith(AndroidJUnit4::class)
-// @SmallTest
+@RunWith(AndroidJUnit4::class)
+@SmallTest
 class CredentialProviderServiceTest {
 
     private val LOG_TAG = "CredentialProviderServiceTest"
 
-    // @Test
+    @Test
     fun test_createRequest() {
         var service = CredentialProviderServiceTestImpl()
         service.isTestMode = true
@@ -54,49 +58,53 @@
         assertThat(service.lastCreateRequest).isNotNull()
     }
 
-    // @Test
+    @Test
     fun test_getRequest() {
         var service = CredentialProviderServiceTestImpl()
         service.isTestMode = true
 
-        var request = BeginGetCredentialRequest(listOf<BeginGetCredentialOption>())
-        val outcome = OutcomeReceiver<androidx.credentials.provider.BeginGetCredentialResponse,
-            androidx.credentials.exceptions.GetCredentialException> {
-            fun onResult(response: androidx.credentials.provider.BeginGetCredentialResponse) {
+        var option = android.service.credentials.BeginGetCredentialOption("id", "type", Bundle())
+        var request = android.service.credentials.BeginGetCredentialRequest.Builder()
+            .setBeginGetCredentialOptions(listOf(option)).build()
+        val outcome = OutcomeReceiver<
+            android.service.credentials.BeginGetCredentialResponse,
+            android.credentials.GetCredentialException> {
+            fun onResult(response: android.service.credentials.BeginGetCredentialResponse) {
                 Log.i(LOG_TAG, "get request: " + response.toString())
             }
 
-            fun onError(error: androidx.credentials.exceptions.GetCredentialException) {
+            fun onError(error: android.credentials.GetCredentialException) {
                 Log.e(LOG_TAG, "get request error", error)
             }
         }
 
         // Call the service.
         assertThat(service.lastGetRequest).isNull()
-        service.onBeginGetCredentialRequest(request, CancellationSignal(), outcome)
+        service.onBeginGetCredential(request, CancellationSignal(), outcome)
         assertThat(service.lastGetRequest).isNotNull()
     }
 
-    // @Test
+    @Test
     fun test_clearRequest() {
         var service = CredentialProviderServiceTestImpl()
         service.isTestMode = true
 
-        var request = ProviderClearCredentialStateRequest(CallingAppInfo("name", SigningInfo()))
-        val outcome = OutcomeReceiver<Void?,
-            androidx.credentials.exceptions.ClearCredentialException> {
-            fun onResult(response: Void?) {
+        var request = android.service.credentials.ClearCredentialStateRequest(
+            android.service.credentials.CallingAppInfo("name", SigningInfo()), Bundle())
+        val outcome = OutcomeReceiver<Void,
+            android.credentials.ClearCredentialStateException> {
+            fun onResult(response: Void) {
                 Log.i(LOG_TAG, "clear request: " + response.toString())
             }
 
-            fun onError(error: androidx.credentials.exceptions.ClearCredentialException) {
+            fun onError(error: android.credentials.ClearCredentialStateException) {
                 Log.e(LOG_TAG, "clear request error", error)
             }
         }
 
         // Call the service.
         assertThat(service.lastClearRequest).isNull()
-        service.onClearCredentialStateRequest(request, CancellationSignal(), outcome)
+        service.onClearCredentialState(request, CancellationSignal(), outcome)
         assertThat(service.lastClearRequest).isNotNull()
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt
index c61120c..2843f46 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt
@@ -241,14 +241,6 @@
         val builder = android.credentials.GetCredentialRequest.Builder(
             GetCredentialRequest.toRequestDataBundle(request))
         request.credentialOptions.forEach {
-            if (request.preferImmediatelyAvailableCredentials &&
-                it is GetPublicKeyCredentialOption) {
-                it.requestData.putBoolean(
-                    "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS",
-                    true,
-                )
-            }
-
             builder.addCredentialOption(
                 android.credentials.CredentialOption.Builder(
                     it.type, it.requestData, it.candidateQueryData
diff --git a/development/referenceDocs/switcher.py b/development/referenceDocs/switcher.py
index 63195ae..562d433 100755
--- a/development/referenceDocs/switcher.py
+++ b/development/referenceDocs/switcher.py
@@ -81,14 +81,14 @@
       stub = doc.replace(java_source_abs_path, kotlin_source_abs_path)
       # Always add the switcher for java files, switch to the package summary if
       # the page itself doesn't exist in kotlin
-      slug1 = "sed -i 's/<\/h1>/{}/' {}".format("<\/h1>\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_java_switcher2.md\" %}",doc)
+      slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\">/{}/' {}".format("\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_java_switcher2.md\" %}",doc)
     else:
       file_path = doc[len(kotlin_ref_root)+1:]
       stub = doc.replace(kotlin_source_abs_path, java_source_abs_path)
       if (both):
-        slug1 = "sed -i 's/<\/h1>/{}/' {}".format("<\/h1>\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
+        slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\">/{}/' {}".format("\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
       else:
-        slug1 = "sed -i 's/<\/h1>/{}/' {}".format("<\/h1>\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
+        slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\">/{}/' {}".format("\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
 
     os.system(slug1)
     if both or java:
diff --git a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
index 35c63ca..ca2a19f 100644
--- a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
+++ b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
@@ -579,97 +579,36 @@
     }
 
     @Test
-    @LargeTest
-    @Ignore("b/290382533")
+    @SmallTest
     public void testDoNotFailOnCorruptedImage() throws Throwable {
-        // ExifInterface shouldn't raise any exceptions except an IOException when unable to open
-        // a file, even with a corrupted image. Generates randomly corrupted image stream for
-        // testing. Uses Epoch date count as random seed so that we can reproduce a broken test.
-        long seed = System.currentTimeMillis() / (86400 * 1000);
-        Log.d(TAG, "testDoNotFailOnCorruptedImage random seed: " + seed);
-        Random random = new Random(seed);
+        Random random = new Random(/* seed= */ 0);
         byte[] bytes = new byte[8096];
+        random.nextBytes(bytes);
+        // Overwrite the start of the random bytes with some JPEG-like data, so it starts like a
+        // plausible image with EXIF data.
         ByteBuffer buffer = ByteBuffer.wrap(bytes);
-        for (int i = 0; i < TEST_NUMBER_OF_CORRUPTED_IMAGE_STREAMS; i++) {
-            buffer.clear();
-            random.nextBytes(bytes);
-            if (!randomlyCorrupted(random)) {
-                buffer.put(ExifInterface.JPEG_SIGNATURE);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.put(ExifInterface.MARKER_APP1);
-            }
-            buffer.putShort((short) (random.nextInt(100) + 300));
-            if (!randomlyCorrupted(random)) {
-                buffer.put(ExifInterface.IDENTIFIER_EXIF_APP1);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.putShort(ExifInterface.BYTE_ALIGN_MM);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.put((byte) 0);
-                buffer.put(ExifInterface.START_CODE);
-            }
-            buffer.putInt(8);
+        buffer.put(ExifInterface.JPEG_SIGNATURE);
+        buffer.put(ExifInterface.MARKER_APP1);
+        buffer.putShort((short) 350);
+        buffer.put(ExifInterface.IDENTIFIER_EXIF_APP1);
+        buffer.putShort(ExifInterface.BYTE_ALIGN_MM);
+        buffer.put((byte) 0);
+        buffer.put(ExifInterface.START_CODE);
+        buffer.putInt(8);
+        // Number of primary tag directories
+        buffer.putShort((short) 1);
+        // Corruption starts here
 
-            // Primary Tags
-            int numberOfDirectory = random.nextInt(8) + 1;
-            if (!randomlyCorrupted(random)) {
-                buffer.putShort((short) numberOfDirectory);
-            }
-            for (int j = 0; j < numberOfDirectory; j++) {
-                generateRandomExifTag(buffer, ExifInterface.IFD_TYPE_PRIMARY, random);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.putInt(buffer.position() - 8);
-            }
-
-            // Thumbnail Tags
-            numberOfDirectory = random.nextInt(8) + 1;
-            if (!randomlyCorrupted(random)) {
-                buffer.putShort((short) numberOfDirectory);
-            }
-            for (int j = 0; j < numberOfDirectory; j++) {
-                generateRandomExifTag(buffer, ExifInterface.IFD_TYPE_THUMBNAIL, random);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.putInt(buffer.position() - 8);
-            }
-
-            // Preview Tags
-            numberOfDirectory = random.nextInt(8) + 1;
-            if (!randomlyCorrupted(random)) {
-                buffer.putShort((short) numberOfDirectory);
-            }
-            for (int j = 0; j < numberOfDirectory; j++) {
-                generateRandomExifTag(buffer, ExifInterface.IFD_TYPE_PREVIEW, random);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.putInt(buffer.position() - 8);
-            }
-
-            if (!randomlyCorrupted(random)) {
-                buffer.put(ExifInterface.MARKER);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.put(ExifInterface.MARKER_EOI);
-            }
-
-            try {
-                new ExifInterface(new ByteArrayInputStream(bytes));
-                // Always success
-            } catch (IOException e) {
-                fail("Should not reach here!");
-            }
-        }
+        ExifInterface exifInterface = new ExifInterface(new ByteArrayInputStream(bytes));
+        exifInterface.getAttribute(ExifInterface.TAG_ARTIST);
+        // Test will fail if the ExifInterface constructor or getter throw an exception.
     }
 
     @Test
     @SmallTest
-    @Ignore("b/290382533")
     public void testSetGpsInfo() throws IOException {
         final String provider = "ExifInterfaceTest";
-        final long timestamp = System.currentTimeMillis();
+        final long timestamp = 1689328448000L; // 2023-07-14T09:54:32.000Z
         final float speedInMeterPerSec = 36.627533f;
         Location location = new Location(provider);
         location.setLatitude(TEST_LATITUDE_VALID_VALUES[TEST_LATITUDE_VALID_VALUES.length - 1]);
@@ -761,7 +700,6 @@
      */
     @Test
     @SmallTest
-    @Ignore("b/290382533")
     public void testGetSetDateTime() throws IOException {
         final long expectedGetDatetimeValue =
                 1454027547000L /* TAG_DATETIME value ("2016:01:29 18:32:27") converted to msec */
@@ -788,12 +726,12 @@
                 exif.getAttribute(ExifInterface.TAG_OFFSET_TIME_DIGITIZED));
 
         // Test setting datetime values
-        final long currentTimeStamp = System.currentTimeMillis();
+        final long newTimestamp = 1689328448000L; // 2023-07-14T09:54:32.000Z
         final long expectedDatetimeOffsetLongValue = 32400000L;
-        exif.setDateTime(currentTimeStamp);
+        exif.setDateTime(newTimestamp);
         exif.saveAttributes();
         exif = new ExifInterface(imageFile.getAbsolutePath());
-        assertEquals(currentTimeStamp - expectedDatetimeOffsetLongValue, (long) exif.getDateTime());
+        assertEquals(newTimestamp - expectedDatetimeOffsetLongValue, (long) exif.getDateTime());
 
         // Test that setting null throws NPE
         try {
@@ -1433,31 +1371,6 @@
         assertEquals(expectedValue.thumbnailHeight, thumbnailBitmap.getHeight());
     }
 
-    private void generateRandomExifTag(ByteBuffer buffer, int ifdType, Random random) {
-        ExifInterface.ExifTag[] tagGroup = ExifInterface.EXIF_TAGS[ifdType];
-        ExifInterface.ExifTag tag = tagGroup[random.nextInt(tagGroup.length)];
-        if (!randomlyCorrupted(random)) {
-            buffer.putShort((short) tag.number);
-        }
-        int dataFormat = random.nextInt(ExifInterface.IFD_FORMAT_NAMES.length);
-        if (!randomlyCorrupted(random)) {
-            buffer.putShort((short) dataFormat);
-        }
-        buffer.putInt(1);
-        int dataLength = ExifInterface.IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
-        if (dataLength > 4) {
-            buffer.putShort((short) random.nextInt(8096 - dataLength));
-            buffer.position(buffer.position() + 2);
-        } else {
-            buffer.position(buffer.position() + 4);
-        }
-    }
-
-    private boolean randomlyCorrupted(Random random) {
-        // Corrupts somewhere in a possibility of 1/500.
-        return random.nextInt(500) == 0;
-    }
-
     private void closeQuietly(Closeable closeable) {
         if (closeable != null) {
             try {
diff --git a/glance/glance-appwidget/build.gradle b/glance/glance-appwidget/build.gradle
index 4b59ee3..a8e7d1c 100644
--- a/glance/glance-appwidget/build.gradle
+++ b/glance/glance-appwidget/build.gradle
@@ -117,7 +117,6 @@
 
 LayoutGeneratorTask.registerLayoutGenerator(
         project,
-        android,
         /* containerLayoutDirectory= */ file("src/main/layoutTemplates"),
         /* childLayoutDirectory= */ file("src/main/res/layout")
 )
diff --git a/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/gradle/LayoutGeneratorTask.kt b/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/gradle/LayoutGeneratorTask.kt
index 75f6129..a54cc8a 100644
--- a/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/gradle/LayoutGeneratorTask.kt
+++ b/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/gradle/LayoutGeneratorTask.kt
@@ -20,7 +20,7 @@
 import androidx.glance.appwidget.layoutgenerator.LayoutGenerator
 import androidx.glance.appwidget.layoutgenerator.cleanResources
 import androidx.glance.appwidget.layoutgenerator.generateRegistry
-import com.android.build.gradle.LibraryExtension
+import com.android.build.api.variant.AndroidComponentsExtension
 import java.io.File
 import org.gradle.api.DefaultTask
 import org.gradle.api.Project
@@ -57,6 +57,10 @@
 
     @TaskAction
     fun execute() {
+
+        outputSourceDir.asFile.get().mkdirs()
+        outputResourcesDir.asFile.get().mkdirs()
+
         val generatedLayouts = LayoutGenerator().generateAllFiles(
             checkNotNull(containerLayoutDirectory.get().asFile.listFiles()).asList(),
             checkNotNull(childLayoutDirectory.get().asFile.listFiles()).asList(),
@@ -70,54 +74,51 @@
             outputSourceDir = outputSourceDir.get().asFile
         )
         cleanResources(
-            outputResourcesDir.get().asFile,
-            generatedLayouts.extractGeneratedFiles()
+            outputResourcesDir.get().asFile, generatedLayouts.extractGeneratedFiles()
         )
     }
 
     private fun GeneratedFiles.extractGeneratedFiles(): Set<File> =
         generatedContainers.values.flatMap { container ->
             container.map { it.generatedFile }
-        }.toSet() +
-        generatedBoxChildren.values.flatMap { child ->
+        }.toSet() + generatedBoxChildren.values.flatMap { child ->
             child.map { it.generatedFile }
-        }.toSet() +
-        generatedRowColumnChildren.values.flatMap { child ->
+        }.toSet() + generatedRowColumnChildren.values.flatMap { child ->
             child.map { it.generatedFile }
-        }.toSet() +
-        extraFiles
+        }.toSet() + extraFiles
 
     companion object {
         /**
-         * Registers [LayoutGeneratorTask] in [project] for all variants in [libraryExtension].
+         * Registers [LayoutGeneratorTask] in [project] for all variants.
          */
         @JvmStatic
         fun registerLayoutGenerator(
             project: Project,
-            libraryExtension: LibraryExtension,
             containerLayoutDirectory: File,
             childLayoutDirectory: File,
         ) {
-            libraryExtension.libraryVariants.all { variant ->
-                val variantName = variant.name
-                val outputDirectory = project.buildDir.resolve("generatedLayouts/$variantName")
-                val outputResourcesDir = outputDirectory.resolve("res/layouts")
-                val outputSourceDir = outputDirectory.resolve("kotlin")
-                val taskName =
-                    "generateLayouts" + variantName.replaceFirstChar { it.uppercaseChar() }
-                outputResourcesDir.mkdirs()
-                outputSourceDir.mkdirs()
-                val task = project.tasks.register(taskName, LayoutGeneratorTask::class.java) {
-                    it.containerLayoutDirectory.set(containerLayoutDirectory)
-                    it.childLayoutDirectory.set(childLayoutDirectory)
-                    it.outputResourcesDir.set(outputResourcesDir)
-                    it.outputSourceDir.set(outputSourceDir)
-                }
-                variant.registerGeneratedResFolders(
-                    project.files(outputResourcesDir).builtBy(task)
-                )
-                variant.registerJavaGeneratingTask(task, outputSourceDir)
+
+            val outputDirectory = "generatedLayouts"
+            val buildDirectory = project.layout.buildDirectory
+
+            val taskName = "generateLayouts"
+
+            val task = project.tasks.register(taskName, LayoutGeneratorTask::class.java) {
+                it.containerLayoutDirectory.set(containerLayoutDirectory)
+                it.childLayoutDirectory.set(childLayoutDirectory)
+                it.outputSourceDir.set(buildDirectory.dir("$outputDirectory/kotlin"))
+                it.outputResourcesDir.set(buildDirectory.dir("$outputDirectory/res/layouts"))
             }
+
+            project.extensions.getByType(AndroidComponentsExtension::class.java)
+                .onVariants { variant ->
+                    variant.sources.java?.addGeneratedSourceDirectory(
+                        task, LayoutGeneratorTask::outputSourceDir
+                    )
+                    variant.sources.res?.addGeneratedSourceDirectory(
+                        task, LayoutGeneratorTask::outputResourcesDir
+                    )
+                }
         }
     }
 }
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/CheckBox.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/CheckBox.kt
index 0ec5659..f9a902b 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/CheckBox.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/CheckBox.kt
@@ -19,6 +19,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
 import androidx.glance.Emittable
+import androidx.glance.EmittableCheckable
 import androidx.glance.ExperimentalGlanceApi
 import androidx.glance.GlanceModifier
 import androidx.glance.GlanceNode
@@ -229,12 +230,8 @@
 
 internal class EmittableCheckBox(
     var colors: CheckBoxColors
-) : Emittable {
+) : EmittableCheckable() {
     override var modifier: GlanceModifier = GlanceModifier
-    var checked: Boolean = false
-    var text: String = ""
-    var style: TextStyle? = null
-    var maxLines: Int = Int.MAX_VALUE
 
     override fun copy(): Emittable = EmittableCheckBox(colors = colors).also {
         it.modifier = modifier
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RadioButton.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RadioButton.kt
index 63e99e3..aca8172 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RadioButton.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RadioButton.kt
@@ -19,6 +19,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
 import androidx.glance.Emittable
+import androidx.glance.EmittableCheckable
 import androidx.glance.ExperimentalGlanceApi
 import androidx.glance.GlanceModifier
 import androidx.glance.GlanceNode
@@ -39,13 +40,9 @@
 
 internal class EmittableRadioButton(
     var colors: RadioButtonColors
-) : Emittable {
+) : EmittableCheckable() {
     override var modifier: GlanceModifier = GlanceModifier
-    var checked: Boolean = false
     var enabled: Boolean = true
-    var text: String = ""
-    var style: TextStyle? = null
-    var maxLines: Int = Int.MAX_VALUE
 
     override fun copy(): Emittable = EmittableRadioButton(colors = colors).also {
         it.modifier = modifier
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/Switch.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/Switch.kt
index 2eb8a9f..dab1746 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/Switch.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/Switch.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.runtime.Composable
 import androidx.glance.Emittable
+import androidx.glance.EmittableCheckable
 import androidx.glance.ExperimentalGlanceApi
 import androidx.glance.GlanceModifier
 import androidx.glance.GlanceNode
@@ -226,12 +227,8 @@
 
 internal class EmittableSwitch(
     var colors: SwitchColors
-) : Emittable {
+) : EmittableCheckable() {
     override var modifier: GlanceModifier = GlanceModifier
-    var checked: Boolean = false
-    var text: String = ""
-    var style: TextStyle? = null
-    var maxLines: Int = Int.MAX_VALUE
 
     override fun copy(): Emittable = EmittableSwitch(colors = colors).also {
         it.modifier = modifier
diff --git a/glance/glance/src/main/java/androidx/glance/Emittables.kt b/glance/glance/src/main/java/androidx/glance/Emittables.kt
index 52a538a..5b03dbf 100644
--- a/glance/glance/src/main/java/androidx/glance/Emittables.kt
+++ b/glance/glance/src/main/java/androidx/glance/Emittables.kt
@@ -18,6 +18,7 @@
 
 import androidx.annotation.RestrictTo
 import androidx.glance.layout.Alignment
+import androidx.glance.text.TextStyle
 
 /** @suppress */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -43,3 +44,17 @@
 abstract class EmittableLazyItemWithChildren : EmittableWithChildren() {
     var alignment: Alignment = Alignment.CenterStart
 }
+
+/** @suppress */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+abstract class EmittableWithText : Emittable {
+    var text: String = ""
+    var style: TextStyle? = null
+    var maxLines: Int = Int.MAX_VALUE
+}
+
+/** @suppress */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+abstract class EmittableCheckable : EmittableWithText() {
+    var checked: Boolean = false
+}
diff --git a/glance/glance/src/main/java/androidx/glance/text/Text.kt b/glance/glance/src/main/java/androidx/glance/text/Text.kt
index ace0cfb..2f9974a 100644
--- a/glance/glance/src/main/java/androidx/glance/text/Text.kt
+++ b/glance/glance/src/main/java/androidx/glance/text/Text.kt
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2021 The Android Open Source Project
  *
@@ -21,6 +20,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
 import androidx.glance.Emittable
+import androidx.glance.EmittableWithText
 import androidx.glance.GlanceModifier
 import androidx.glance.GlanceNode
 import androidx.glance.text.TextDefaults.defaultTextStyle
@@ -60,11 +60,8 @@
 
 /** @suppress */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class EmittableText : Emittable {
+class EmittableText : EmittableWithText() {
     override var modifier: GlanceModifier = GlanceModifier
-    var text: String = ""
-    var style: TextStyle? = null
-    var maxLines: Int = Int.MAX_VALUE
 
     override fun copy(): Emittable = EmittableText().also {
         it.modifier = modifier
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index d8009d4..a4025c9 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -104,7 +104,7 @@
 checkerframework = { module = "org.checkerframework:checker-qual", version = "2.5.3" }
 checkmark = { module = "net.saff.checkmark:checkmark", version = "0.1.6" }
 constraintLayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.0.1"}
-dackka = { module = "com.google.devsite:dackka", version = "1.3.4" }
+dackka = { module = "com.google.devsite:dackka", version = "1.3.5" }
 dagger = { module = "com.google.dagger:dagger", version.ref = "dagger" }
 daggerCompiler = { module = "com.google.dagger:dagger-compiler", version.ref = "dagger" }
 dexmakerMockito = { module = "com.linkedin.dexmaker:dexmaker-mockito", version.ref = "dexmaker" }
diff --git a/health/connect/connect-client/api/current.txt b/health/connect/connect-client/api/current.txt
index 9332a76..e82f8fb 100644
--- a/health/connect/connect-client/api/current.txt
+++ b/health/connect/connect-client/api/current.txt
@@ -109,10 +109,10 @@
 
 package androidx.health.connect.client.contracts {
 
-  public final class ExerciseRouteRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,androidx.health.connect.client.records.ExerciseRoute.Data> {
+  public final class ExerciseRouteRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,androidx.health.connect.client.records.ExerciseRoute> {
     ctor public ExerciseRouteRequestContract();
     method public android.content.Intent createIntent(android.content.Context context, String input);
-    method public androidx.health.connect.client.records.ExerciseRoute.Data? parseResult(int resultCode, android.content.Intent? intent);
+    method public androidx.health.connect.client.records.ExerciseRoute? parseResult(int resultCode, android.content.Intent? intent);
   }
 
   public final class HealthPermissionsRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.util.Set<? extends java.lang.String>,java.util.Set<? extends java.lang.String>> {
@@ -442,15 +442,8 @@
     property public final java.time.Instant startTime;
   }
 
-  public abstract class ExerciseRoute {
-  }
-
-  public static final class ExerciseRoute.ConsentRequired extends androidx.health.connect.client.records.ExerciseRoute {
-    ctor public ExerciseRoute.ConsentRequired();
-  }
-
-  public static final class ExerciseRoute.Data extends androidx.health.connect.client.records.ExerciseRoute {
-    ctor public ExerciseRoute.Data(java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route);
+  public final class ExerciseRoute {
+    ctor public ExerciseRoute(java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route);
     method public java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> getRoute();
     property public final java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route;
   }
@@ -475,8 +468,21 @@
   public static final class ExerciseRoute.Location.Companion {
   }
 
-  public static final class ExerciseRoute.NoData extends androidx.health.connect.client.records.ExerciseRoute {
-    ctor public ExerciseRoute.NoData();
+  public abstract class ExerciseRouteResult {
+  }
+
+  public static final class ExerciseRouteResult.ConsentRequired extends androidx.health.connect.client.records.ExerciseRouteResult {
+    ctor public ExerciseRouteResult.ConsentRequired();
+  }
+
+  public static final class ExerciseRouteResult.Data extends androidx.health.connect.client.records.ExerciseRouteResult {
+    ctor public ExerciseRouteResult.Data(androidx.health.connect.client.records.ExerciseRoute exerciseRoute);
+    method public androidx.health.connect.client.records.ExerciseRoute getExerciseRoute();
+    property public final androidx.health.connect.client.records.ExerciseRoute exerciseRoute;
+  }
+
+  public static final class ExerciseRouteResult.NoData extends androidx.health.connect.client.records.ExerciseRouteResult {
+    ctor public ExerciseRouteResult.NoData();
   }
 
   public final class ExerciseSegment {
@@ -570,10 +576,10 @@
     ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments);
     ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps);
-    ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps, optional androidx.health.connect.client.records.ExerciseRoute.Data? exerciseRouteData);
+    ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps, optional androidx.health.connect.client.records.ExerciseRoute? exerciseRoute);
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
-    method public androidx.health.connect.client.records.ExerciseRoute getExerciseRoute();
+    method public androidx.health.connect.client.records.ExerciseRouteResult getExerciseRouteResult();
     method public int getExerciseType();
     method public java.util.List<androidx.health.connect.client.records.ExerciseLap> getLaps();
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
@@ -584,7 +590,7 @@
     method public String? getTitle();
     property public java.time.Instant endTime;
     property public java.time.ZoneOffset? endZoneOffset;
-    property public final androidx.health.connect.client.records.ExerciseRoute exerciseRoute;
+    property public final androidx.health.connect.client.records.ExerciseRouteResult exerciseRouteResult;
     property public final int exerciseType;
     property public final java.util.List<androidx.health.connect.client.records.ExerciseLap> laps;
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
diff --git a/health/connect/connect-client/api/restricted_current.txt b/health/connect/connect-client/api/restricted_current.txt
index 41f97b3..582c44b 100644
--- a/health/connect/connect-client/api/restricted_current.txt
+++ b/health/connect/connect-client/api/restricted_current.txt
@@ -109,10 +109,10 @@
 
 package androidx.health.connect.client.contracts {
 
-  public final class ExerciseRouteRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,androidx.health.connect.client.records.ExerciseRoute.Data> {
+  public final class ExerciseRouteRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,androidx.health.connect.client.records.ExerciseRoute> {
     ctor public ExerciseRouteRequestContract();
     method public android.content.Intent createIntent(android.content.Context context, String input);
-    method public androidx.health.connect.client.records.ExerciseRoute.Data? parseResult(int resultCode, android.content.Intent? intent);
+    method public androidx.health.connect.client.records.ExerciseRoute? parseResult(int resultCode, android.content.Intent? intent);
   }
 
   public final class HealthPermissionsRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.util.Set<? extends java.lang.String>,java.util.Set<? extends java.lang.String>> {
@@ -442,15 +442,8 @@
     property public final java.time.Instant startTime;
   }
 
-  public abstract class ExerciseRoute {
-  }
-
-  public static final class ExerciseRoute.ConsentRequired extends androidx.health.connect.client.records.ExerciseRoute {
-    ctor public ExerciseRoute.ConsentRequired();
-  }
-
-  public static final class ExerciseRoute.Data extends androidx.health.connect.client.records.ExerciseRoute {
-    ctor public ExerciseRoute.Data(java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route);
+  public final class ExerciseRoute {
+    ctor public ExerciseRoute(java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route);
     method public java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> getRoute();
     property public final java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route;
   }
@@ -475,8 +468,21 @@
   public static final class ExerciseRoute.Location.Companion {
   }
 
-  public static final class ExerciseRoute.NoData extends androidx.health.connect.client.records.ExerciseRoute {
-    ctor public ExerciseRoute.NoData();
+  public abstract class ExerciseRouteResult {
+  }
+
+  public static final class ExerciseRouteResult.ConsentRequired extends androidx.health.connect.client.records.ExerciseRouteResult {
+    ctor public ExerciseRouteResult.ConsentRequired();
+  }
+
+  public static final class ExerciseRouteResult.Data extends androidx.health.connect.client.records.ExerciseRouteResult {
+    ctor public ExerciseRouteResult.Data(androidx.health.connect.client.records.ExerciseRoute exerciseRoute);
+    method public androidx.health.connect.client.records.ExerciseRoute getExerciseRoute();
+    property public final androidx.health.connect.client.records.ExerciseRoute exerciseRoute;
+  }
+
+  public static final class ExerciseRouteResult.NoData extends androidx.health.connect.client.records.ExerciseRouteResult {
+    ctor public ExerciseRouteResult.NoData();
   }
 
   public final class ExerciseSegment {
@@ -570,10 +576,10 @@
     ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments);
     ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps);
-    ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps, optional androidx.health.connect.client.records.ExerciseRoute.Data? exerciseRouteData);
+    ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps, optional androidx.health.connect.client.records.ExerciseRoute? exerciseRoute);
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
-    method public androidx.health.connect.client.records.ExerciseRoute getExerciseRoute();
+    method public androidx.health.connect.client.records.ExerciseRouteResult getExerciseRouteResult();
     method public int getExerciseType();
     method public java.util.List<androidx.health.connect.client.records.ExerciseLap> getLaps();
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
@@ -584,7 +590,7 @@
     method public String? getTitle();
     property public java.time.Instant endTime;
     property public java.time.ZoneOffset? endZoneOffset;
-    property public final androidx.health.connect.client.records.ExerciseRoute exerciseRoute;
+    property public final androidx.health.connect.client.records.ExerciseRouteResult exerciseRouteResult;
     property public final int exerciseType;
     property public final java.util.List<androidx.health.connect.client.records.ExerciseLap> laps;
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
diff --git a/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/InsertRecordsSamples.kt b/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/InsertRecordsSamples.kt
index 4361146..f22f57d 100644
--- a/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/InsertRecordsSamples.kt
+++ b/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/InsertRecordsSamples.kt
@@ -113,9 +113,9 @@
     val latitudeDeltaPerSecond = (endLatitude - startLatitude) / sessionDuration.seconds
     val longitudeDeltaPerSecond = (endLongitude - startLongitude) / sessionDuration.seconds
 
-    val exerciseRouteData =
+    val exerciseRoute =
         if (grantedPermissions.contains(PERMISSION_WRITE_EXERCISE_ROUTE)) {
-            ExerciseRoute.Data(
+            ExerciseRoute(
                 List(sessionDuration.seconds.toInt()) { timeSeconds ->
                     ExerciseRoute.Location(
                         time = sessionStartTime.plusSeconds(timeSeconds.toLong()),
@@ -140,7 +140,7 @@
             exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
             title = "Morning Run",
             notes = "A nice run in a park",
-            exerciseRouteData = exerciseRouteData
+            exerciseRoute = exerciseRoute
         )
 
     healthConnectClient.insertRecords(listOf(exerciseSessionRecord))
diff --git a/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/ReadRecordsSamples.kt b/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/ReadRecordsSamples.kt
index 8f5cca7..578fd0e 100644
--- a/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/ReadRecordsSamples.kt
+++ b/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/ReadRecordsSamples.kt
@@ -23,6 +23,7 @@
 import androidx.health.connect.client.HealthConnectClient
 import androidx.health.connect.client.contracts.ExerciseRouteRequestContract
 import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
 import androidx.health.connect.client.records.ExerciseSessionRecord
 import androidx.health.connect.client.records.HeartRateRecord
 import androidx.health.connect.client.records.SleepSessionRecord
@@ -86,14 +87,14 @@
 suspend fun ReadExerciseRoute(
     activityResultCaller: ActivityResultCaller,
     healthConnectClient: HealthConnectClient,
-    displayExerciseRoute: (ExerciseRoute.Data) -> Unit,
+    displayExerciseRoute: (ExerciseRoute) -> Unit,
     recordId: String
 ) {
     // See https://developer.android.com/training/basics/intents/result#launch for appropriately
     // handling ActivityResultContract.
     val requestExerciseRoute =
         activityResultCaller.registerForActivityResult(ExerciseRouteRequestContract()) {
-            exerciseRoute: ExerciseRoute.Data? ->
+            exerciseRoute: ExerciseRoute? ->
             if (exerciseRoute != null) {
                 displayExerciseRoute(exerciseRoute)
             } else {
@@ -105,10 +106,10 @@
     val exerciseSessionRecord =
         healthConnectClient.readRecord(ExerciseSessionRecord::class, recordId).record
 
-    when (val exerciseRoute = exerciseSessionRecord.exerciseRoute) {
-        is ExerciseRoute.Data -> displayExerciseRoute(exerciseRoute)
-        is ExerciseRoute.ConsentRequired -> requestExerciseRoute.launch(recordId)
-        is ExerciseRoute.NoData -> Unit // No exercise route to show
+    when (val exerciseRouteResult = exerciseSessionRecord.exerciseRouteResult) {
+        is ExerciseRouteResult.Data -> displayExerciseRoute(exerciseRouteResult.exerciseRoute)
+        is ExerciseRouteResult.ConsentRequired -> requestExerciseRoute.launch(recordId)
+        is ExerciseRouteResult.NoData -> Unit // No exercise route to show
         else -> Unit
     }
 }
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt
index 23160ad..ea4548d 100644
--- a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt
@@ -80,7 +80,7 @@
         val intent = Intent()
         intent.putExtra(HealthConnectManager.EXTRA_EXERCISE_ROUTE, PlatformExerciseRoute(listOf()))
         val result = requestRouteContract.parseResult(0, intent)
-        assertThat(result).isEqualTo(ExerciseRoute.Data(listOf()))
+        assertThat(result).isEqualTo(ExerciseRoute(listOf()))
     }
 
     @Test
@@ -103,7 +103,7 @@
         val result = requestRouteContract.parseResult(0, intent)
         assertThat(result)
             .isEqualTo(
-                ExerciseRoute.Data(
+                ExerciseRoute(
                     listOf(
                         ExerciseRoute.Location(
                             time = Instant.ofEpochMilli(1234L),
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt
index d0cf773..953b19b 100644
--- a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt
@@ -35,6 +35,7 @@
 import androidx.health.connect.client.records.ElevationGainedRecord
 import androidx.health.connect.client.records.ExerciseLap
 import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
 import androidx.health.connect.client.records.ExerciseSegment
 import androidx.health.connect.client.records.ExerciseSessionRecord
 import androidx.health.connect.client.records.FloorsClimbedRecord
@@ -408,7 +409,7 @@
                             )
                         ),
                     exerciseRoute =
-                        ExerciseRoute.Data(
+                        ExerciseRoute(
                             listOf(
                                 ExerciseRoute.Location(
                                     START_TIME,
@@ -1261,17 +1262,19 @@
                         10
                     )
                 )
-            assertThat(exerciseRoute as ExerciseRoute.Data)
+            assertThat(exerciseRouteResult as ExerciseRouteResult.Data)
                 .isEqualTo(
-                    ExerciseRoute.Data(
-                        listOf(
-                            ExerciseRoute.Location(
-                                time = START_TIME,
-                                latitude = 23.4,
-                                longitude = -23.4,
-                                altitude = Length.meters(10.0),
-                                horizontalAccuracy = Length.meters(2.0),
-                                verticalAccuracy = Length.meters(3.0)
+                    ExerciseRouteResult.Data(
+                        ExerciseRoute(
+                            listOf(
+                                ExerciseRoute.Location(
+                                    time = START_TIME,
+                                    latitude = 23.4,
+                                    longitude = -23.4,
+                                    altitude = Length.meters(10.0),
+                                    horizontalAccuracy = Length.meters(2.0),
+                                    verticalAccuracy = Length.meters(3.0)
+                                )
                             )
                         )
                     )
@@ -1283,7 +1286,7 @@
                 as ExerciseSessionRecord
 
         assertSdkRecord(sdkExerciseSession) {
-            assertThat(exerciseRoute).isEqualTo(ExerciseRoute.NoData())
+            assertThat(exerciseRouteResult).isEqualTo(ExerciseRouteResult.NoData())
         }
     }
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/contracts/ExerciseRouteRequestContract.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/contracts/ExerciseRouteRequestContract.kt
index 6e8e74b..0e1e78f 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/contracts/ExerciseRouteRequestContract.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/contracts/ExerciseRouteRequestContract.kt
@@ -32,9 +32,9 @@
  *
  * @sample androidx.health.connect.client.samples.ReadExerciseRoute
  */
-class ExerciseRouteRequestContract : ActivityResultContract<String, ExerciseRoute.Data?>() {
+class ExerciseRouteRequestContract : ActivityResultContract<String, ExerciseRoute?>() {
 
-    private val delegate: ActivityResultContract<String, ExerciseRoute.Data?> =
+    private val delegate: ActivityResultContract<String, ExerciseRoute?> =
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
             RequestExerciseRouteUpsideDownCake()
         } else {
@@ -42,7 +42,7 @@
         }
 
     /**
-     * Creates an intent to request an [ExerciseRoute.Data]. It receives the exercise session id as
+     * Creates an intent to request an [ExerciseRoute]. It receives the exercise session id as
      * [input].
      *
      * @param context the context
@@ -56,13 +56,13 @@
     }
 
     /**
-     * Converts the activity result into [ExerciseRoute.Data], to return as output.
+     * Converts the activity result into [ExerciseRoute], to return as output.
      *
      * @return null if the user didn't grant access to the exercise route or if there's no exercise
      *   route for the session id passed on [createIntent].
      * @see ActivityResultContract.parseResult
      */
-    override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute.Data? {
+    override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute? {
         return delegate.parseResult(resultCode, intent)
     }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
index dc60de8..a7f7929 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
@@ -35,6 +35,7 @@
 import androidx.health.connect.client.records.DistanceRecord
 import androidx.health.connect.client.records.ElevationGainedRecord
 import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
 import androidx.health.connect.client.records.ExerciseSessionRecord
 import androidx.health.connect.client.records.FloorsClimbedRecord
 import androidx.health.connect.client.records.HeartRateRecord
@@ -421,13 +422,13 @@
                     metadata = metadata,
                     segments = subTypeDataListsMap["segments"]?.toSegmentList() ?: emptyList(),
                     laps = subTypeDataListsMap["laps"]?.toLapList() ?: emptyList(),
-                    exerciseRoute =
+                    exerciseRouteResult =
                         subTypeDataListsMap["route"]?.let {
-                            ExerciseRoute.Data(route = it.toLocationList())
+                            ExerciseRouteResult.Data(ExerciseRoute(route = it.toLocationList()))
                         }
                             ?: if (valuesMap["hasRoute"]?.booleanVal == true)
-                                ExerciseRoute.ConsentRequired()
-                            else ExerciseRoute.NoData(),
+                                ExerciseRouteResult.ConsentRequired()
+                            else ExerciseRouteResult.NoData(),
                 )
             }
             "Distance" ->
@@ -587,8 +588,8 @@
 
 fun toExerciseRouteData(
     protoWrapper: androidx.health.platform.client.exerciseroute.ExerciseRoute
-): ExerciseRoute.Data {
-    return ExerciseRoute.Data(
+): ExerciseRoute {
+    return ExerciseRoute(
         protoWrapper.proto.valuesList.map { value ->
             ExerciseRoute.Location(
                 time = Instant.ofEpochMilli(value.startTimeMillis),
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordUtils.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordUtils.kt
index f2fa0f4..168a416 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordUtils.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordUtils.kt
@@ -119,17 +119,17 @@
             startTime = Instant.ofEpochMilli(it.startTimeMillis),
             endTime = Instant.ofEpochMilli(it.endTimeMillis),
             stage = STAGE_TYPE_STRING_TO_INT_MAP[it.valuesMap["stage"]?.enumVal]
-                ?: SleepSessionRecord.STAGE_TYPE_UNKNOWN
+                    ?: SleepSessionRecord.STAGE_TYPE_UNKNOWN
         )
     }
 }
+
 internal fun DataProto.DataPoint.SubTypeDataList.toSegmentList(): List<ExerciseSegment> {
     return valuesList.map {
         ExerciseSegment(
             startTime = Instant.ofEpochMilli(it.startTimeMillis),
             endTime = Instant.ofEpochMilli(it.endTimeMillis),
-            segmentType = (it.valuesMap["type"]?.longVal
-                ?: EXERCISE_SEGMENT_TYPE_UNKNOWN).toInt(),
+            segmentType = (it.valuesMap["type"]?.longVal ?: EXERCISE_SEGMENT_TYPE_UNKNOWN).toInt(),
             repetitions = it.valuesMap["reps"]?.longVal?.toInt() ?: 0
         )
     }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt
index e9e29d8..34127fc 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt
@@ -34,7 +34,7 @@
 import androidx.health.connect.client.records.CyclingPedalingCadenceRecord
 import androidx.health.connect.client.records.DistanceRecord
 import androidx.health.connect.client.records.ElevationGainedRecord
-import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
 import androidx.health.connect.client.records.ExerciseSessionRecord
 import androidx.health.connect.client.records.FloorsClimbedRecord
 import androidx.health.connect.client.records.HeartRateRecord
@@ -289,7 +289,7 @@
         is ExerciseSessionRecord ->
             intervalProto()
                 .setDataType(protoDataType("ActivitySession"))
-                .putValues("hasRoute", boolVal(exerciseRoute !is ExerciseRoute.NoData))
+                .putValues("hasRoute", boolVal(exerciseRouteResult !is ExerciseRouteResult.NoData))
                 .apply {
                     val exerciseType =
                         enumValFromInt(
@@ -316,11 +316,13 @@
                                 .build()
                         )
                     }
-                    if (exerciseRoute is ExerciseRoute.Data) {
+                    if (exerciseRouteResult is ExerciseRouteResult.Data) {
                         putSubTypeDataLists(
                             "route",
                             DataProto.DataPoint.SubTypeDataList.newBuilder()
-                                .addAllValues(exerciseRoute.route.map { it.toProto() })
+                                .addAllValues(
+                                    exerciseRouteResult.exerciseRoute.route.map { it.toProto() }
+                                )
                                 .build()
                         )
                     }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoUtils.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoUtils.kt
index 8c1cbe2..3e5fa01 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoUtils.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoUtils.kt
@@ -100,8 +100,10 @@
             enumValFromInt(stage, SleepSessionRecord.STAGE_TYPE_INT_TO_STRING_MAP)?.let {
                 putValues("stage", it)
             }
-        }.build()
+        }
+        .build()
 }
+
 internal fun ExerciseSegment.toProto(): DataProto.SubTypeDataValue {
     return DataProto.SubTypeDataValue.newBuilder()
         .setStartTimeMillis(startTime.toEpochMilli())
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt
index 4da86be..ad43fd6 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt
@@ -36,6 +36,7 @@
 import androidx.health.connect.client.records.ElevationGainedRecord
 import androidx.health.connect.client.records.ExerciseLap
 import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
 import androidx.health.connect.client.records.ExerciseSegment
 import androidx.health.connect.client.records.ExerciseSessionRecord
 import androidx.health.connect.client.records.FloorsClimbedRecord
@@ -293,8 +294,9 @@
         laps = laps.map { it.toSdkExerciseLap() }.sortedBy { it.startTime },
         segments = segments.map { it.toSdkExerciseSegment() }.sortedBy { it.startTime },
         metadata = metadata.toSdkMetadata(),
-        exerciseRoute = route?.toSdkExerciseRouteData()
-                ?: if (hasRoute()) ExerciseRoute.ConsentRequired() else ExerciseRoute.NoData(),
+        exerciseRouteResult = route?.let { ExerciseRouteResult.Data(it.toSdkExerciseRoute()) }
+                ?: if (hasRoute()) ExerciseRouteResult.ConsentRequired()
+                else ExerciseRouteResult.NoData(),
     )
 
 private fun PlatformFloorsClimbedRecord.toSdkFloorsClimbedRecord() =
@@ -708,8 +710,8 @@
             title?.let { setTitle(it) }
             setLaps(laps.map { it.toPlatformExerciseLap() })
             setSegments(segments.map { it.toPlatformExerciseSegment() })
-            if (exerciseRoute is ExerciseRoute.Data) {
-                setRoute(exerciseRoute.toPlatformExerciseRoute())
+            if (exerciseRouteResult is ExerciseRouteResult.Data) {
+                setRoute(exerciseRouteResult.exerciseRoute.toPlatformExerciseRoute())
             }
         }
         .build()
@@ -719,7 +721,7 @@
         .apply { length?.let { setLength(it.toPlatformLength()) } }
         .build()
 
-private fun ExerciseRoute.Data.toPlatformExerciseRoute() =
+private fun ExerciseRoute.toPlatformExerciseRoute() =
     PlatformExerciseRoute(
         route.map { location ->
             PlatformExerciseRouteLocationBuilder(
@@ -1033,8 +1035,8 @@
 private fun PlatformSleepSessionStage.toSdkSleepSessionStage() =
     SleepSessionRecord.Stage(startTime, endTime, type.toSdkSleepStageType())
 
-internal fun PlatformExerciseRoute.toSdkExerciseRouteData() =
-    ExerciseRoute.Data(
+internal fun PlatformExerciseRoute.toSdkExerciseRoute() =
+    ExerciseRoute(
         routeLocations.map { value ->
             ExerciseRoute.Location(
                 time = value.time,
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/RequestExerciseRouteInternal.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/RequestExerciseRouteInternal.kt
index 1fdba9c..b96640c 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/RequestExerciseRouteInternal.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/RequestExerciseRouteInternal.kt
@@ -32,8 +32,7 @@
  * @see androidx.activity.ComponentActivity.registerForActivityResult
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-internal class RequestExerciseRouteInternal :
-    ActivityResultContract<String, ExerciseRoute.Data?>() {
+internal class RequestExerciseRouteInternal : ActivityResultContract<String, ExerciseRoute?>() {
     override fun createIntent(context: Context, input: String): Intent {
         require(input.isNotEmpty()) { "Session identifier can't be empty" }
         return Intent(HealthDataServiceConstants.ACTION_REQUEST_ROUTE).apply {
@@ -42,7 +41,7 @@
     }
 
     @Suppress("DEPRECATION") // getParcelableExtra
-    override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute.Data? {
+    override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute? {
         val route =
             intent?.getParcelableExtra<androidx.health.platform.client.exerciseroute.ExerciseRoute>(
                 HealthDataServiceConstants.EXTRA_EXERCISE_ROUTE
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt
index f1a435a..c5ec226 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt
@@ -24,7 +24,7 @@
 import androidx.annotation.RestrictTo
 import androidx.health.connect.client.HealthConnectClient
 import androidx.health.connect.client.impl.platform.records.PlatformExerciseRoute
-import androidx.health.connect.client.impl.platform.records.toSdkExerciseRouteData
+import androidx.health.connect.client.impl.platform.records.toSdkExerciseRoute
 import androidx.health.connect.client.records.ExerciseRoute
 import androidx.health.platform.client.impl.logger.Logger
 
@@ -36,7 +36,7 @@
 @RequiresApi(34)
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 internal class RequestExerciseRouteUpsideDownCake :
-    ActivityResultContract<String, ExerciseRoute.Data?>() {
+    ActivityResultContract<String, ExerciseRoute?>() {
     override fun createIntent(context: Context, input: String): Intent {
         require(input.isNotEmpty()) { "Session identifier can't be empty" }
         return Intent(HealthConnectManager.ACTION_REQUEST_EXERCISE_ROUTE).apply {
@@ -44,7 +44,7 @@
         }
     }
 
-    override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute.Data? {
+    override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute? {
         val route =
             intent?.getParcelableExtra(
                 HealthConnectManager.EXTRA_EXERCISE_ROUTE,
@@ -55,6 +55,6 @@
             return null
         }
         Logger.debug(HealthConnectClient.HEALTH_CONNECT_CLIENT_TAG, "Returned a route.")
-        return route.toSdkExerciseRouteData()
+        return route.toSdkExerciseRoute()
     }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
index 6f36983..679eddb 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
@@ -19,63 +19,37 @@
 import androidx.health.connect.client.units.Length
 import java.time.Instant
 
-/** Captures a route associated with an exercise session a user does. */
-abstract class ExerciseRoute internal constructor() {
-
-    /**
-     * Class containing data of an exercise route.
-     *
-     * Contains a sequence of location points, with timestamps, which do not have to be in order.
-     *
-     * Location points contain a timestamp, longitude, latitude, and optionally altitude, horizontal
-     * and vertical accuracy.
-     */
-    class Data constructor(val route: List<Location>) : ExerciseRoute() {
-        init {
-            val sortedRoute: List<Location> = route.sortedBy { it.time }
-            for (i in 0 until sortedRoute.lastIndex) {
-                require(sortedRoute[i].time.isBefore(sortedRoute[i + 1].time))
-            }
-        }
-
-        internal fun isWithin(startTime: Instant, endTime: Instant): Boolean {
-            val minTime = route.minBy { it.time }.time
-            val maxTime = route.maxBy { it.time }.time
-            return !minTime.isBefore(startTime) && maxTime.isBefore(endTime)
-        }
-
-        override fun equals(other: Any?): Boolean {
-            if (this === other) return true
-            if (other !is Data) return false
-
-            return route == other.route
-        }
-
-        override fun hashCode(): Int {
-            return route.hashCode()
+/**
+ * Captures a route associated with an exercise session a user does.
+ *
+ * Contains a sequence of location points, with timestamps, which do not have to be in order.
+ *
+ * Location points contain a timestamp, longitude, latitude, and optionally altitude, horizontal and
+ * vertical accuracy.
+ */
+class ExerciseRoute constructor(val route: List<Location>) {
+    init {
+        val sortedRoute: List<Location> = route.sortedBy { it.time }
+        for (i in 0 until sortedRoute.lastIndex) {
+            require(sortedRoute[i].time.isBefore(sortedRoute[i + 1].time))
         }
     }
 
-    /** Class indicating that a permission hasn't been granted and a value couldn't be returned. */
-    class ConsentRequired : ExerciseRoute() {
-        override fun equals(other: Any?): Boolean {
-            return other is ConsentRequired
-        }
-
-        override fun hashCode(): Int {
-            return 0
-        }
+    internal fun isWithin(startTime: Instant, endTime: Instant): Boolean {
+        val minTime = route.minBy { it.time }.time
+        val maxTime = route.maxBy { it.time }.time
+        return !minTime.isBefore(startTime) && maxTime.isBefore(endTime)
     }
 
-    /** Class indicating that there's no data to request permissions for. */
-    class NoData : ExerciseRoute() {
-        override fun equals(other: Any?): Boolean {
-            return other is NoData
-        }
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is ExerciseRoute) return false
 
-        override fun hashCode(): Int {
-            return 0
-        }
+        return route == other.route
+    }
+
+    override fun hashCode(): Int {
+        return route.hashCode()
     }
 
     /**
@@ -88,7 +62,7 @@
      * @param horizontalAccuracy in [Length] unit. Optional field. Valid range: non-negative
      *   numbers.
      * @param verticalAccuracy in [Length] unit. Optional field. Valid range: non-negative numbers.
-     * @see ExerciseRoute
+     * @see ExerciseRouteResult
      */
     class Location(
         val time: Instant,
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRouteResult.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRouteResult.kt
new file mode 100644
index 0000000..3978f5c
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRouteResult.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 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.records
+
+/** Result of the route associated with an exercise session a user does. */
+abstract class ExerciseRouteResult internal constructor() {
+
+    /** Class containing data for an [ExerciseRoute]. */
+    class Data(val exerciseRoute: ExerciseRoute) : ExerciseRouteResult() {
+
+        override fun equals(other: Any?): Boolean {
+            if (other !is Data) {
+                return false
+            }
+            return exerciseRoute == other.exerciseRoute
+        }
+
+        override fun hashCode(): Int {
+            return 0
+        }
+    }
+
+    /** Class indicating that a permission hasn't been granted and a value couldn't be returned. */
+    class ConsentRequired : ExerciseRouteResult() {
+        override fun equals(other: Any?): Boolean {
+            return other is ConsentRequired
+        }
+
+        override fun hashCode(): Int {
+            return 0
+        }
+    }
+
+    /** Class indicating that there's no data to request permissions for. */
+    class NoData : ExerciseRouteResult() {
+        override fun equals(other: Any?): Boolean {
+            return other is NoData
+        }
+
+        override fun hashCode(): Int {
+            return 0
+        }
+    }
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
index 1a3747c..8a33653 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
@@ -59,8 +59,8 @@
      */
     val laps: List<ExerciseLap> = emptyList(),
 
-    /** [ExerciseRoute] [ExerciseRoute] of the session. */
-    val exerciseRoute: ExerciseRoute = ExerciseRoute.NoData(),
+    /** [ExerciseRouteResult] [ExerciseRouteResult] of the session. */
+    val exerciseRouteResult: ExerciseRouteResult = ExerciseRouteResult.NoData(),
 ) : IntervalRecord {
 
     @JvmOverloads
@@ -78,7 +78,7 @@
         metadata: Metadata = Metadata.EMPTY,
         segments: List<ExerciseSegment> = emptyList(),
         laps: List<ExerciseLap> = emptyList(),
-        exerciseRouteData: ExerciseRoute.Data? = null,
+        exerciseRoute: ExerciseRoute? = null,
     ) : this(
         startTime,
         startZoneOffset,
@@ -90,7 +90,7 @@
         metadata,
         segments,
         laps,
-        exerciseRouteData ?: ExerciseRoute.NoData()
+        exerciseRoute?.let { ExerciseRouteResult.Data(it) } ?: ExerciseRouteResult.NoData()
     )
 
     init {
@@ -130,8 +130,8 @@
                 "laps can not be out of parent time range."
             }
         }
-        if (exerciseRoute is ExerciseRoute.Data) {
-            require(exerciseRoute.isWithin(startTime, endTime)) {
+        if (exerciseRouteResult is ExerciseRouteResult.Data) {
+            require(exerciseRouteResult.exerciseRoute.isWithin(startTime, endTime)) {
                 "route can not be out of parent time range."
             }
         }
@@ -151,7 +151,7 @@
         if (metadata != other.metadata) return false
         if (segments != other.segments) return false
         if (laps != other.laps) return false
-        if (exerciseRoute != other.exerciseRoute) return false
+        if (exerciseRouteResult != other.exerciseRouteResult) return false
 
         return true
     }
@@ -164,7 +164,7 @@
         result = 31 * result + endTime.hashCode()
         result = 31 * result + (endZoneOffset?.hashCode() ?: 0)
         result = 31 * result + metadata.hashCode()
-        result = 31 * result + exerciseRoute.hashCode()
+        result = 31 * result + exerciseRouteResult.hashCode()
         return result
     }
 
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
index ac86a98..2bd6f9b 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
@@ -643,7 +643,7 @@
                         )
                     ),
                 exerciseRoute =
-                    ExerciseRoute.Data(
+                    ExerciseRoute(
                         route =
                             listOf(
                                 ExerciseRoute.Location(
@@ -676,7 +676,7 @@
                 startZoneOffset = null,
                 endTime = END_TIME,
                 endZoneOffset = null,
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
 
         checkProtoAndRecordTypeNameMatch(data)
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/RequestExerciseRouteInternalTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/RequestExerciseRouteInternalTest.kt
index ffa47f9..b650485 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/RequestExerciseRouteInternalTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/RequestExerciseRouteInternalTest.kt
@@ -73,7 +73,7 @@
             )
         )
         val result = requestRouteContract.parseResult(0, intent)
-        assertThat(result).isEqualTo(ExerciseRoute.Data(listOf()))
+        assertThat(result).isEqualTo(ExerciseRoute(listOf()))
     }
 
     @Test
@@ -114,7 +114,7 @@
         val result = requestRouteContract.parseResult(0, intent)
         assertThat(result)
             .isEqualTo(
-                ExerciseRoute.Data(
+                ExerciseRoute(
                     listOf(
                         ExerciseRoute.Location(
                             time = Instant.ofEpochMilli(1234L),
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
index 46afdf5..35275bf 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
@@ -74,7 +74,7 @@
 
     @Test
     fun emptyRoute() {
-        assertThat(ExerciseRoute.Data(listOf())).isEqualTo(ExerciseRoute.Data(listOf()))
+        assertThat(ExerciseRoute(listOf())).isEqualTo(ExerciseRoute(listOf()))
     }
 
     @Test
@@ -94,8 +94,8 @@
                 latitude = 34.8,
                 longitude = -34.8,
             )
-        assertThat(ExerciseRoute.Data(listOf(location1, location2)))
-            .isEqualTo(ExerciseRoute.Data(listOf(location1, location2)))
+        assertThat(ExerciseRoute(listOf(location1, location2)))
+            .isEqualTo(ExerciseRoute(listOf(location1, location2)))
     }
 
     @Test
@@ -112,8 +112,6 @@
                 latitude = 34.8,
                 longitude = -34.8,
             )
-        assertFailsWith<IllegalArgumentException> {
-            ExerciseRoute.Data(listOf(location1, location2))
-        }
+        assertFailsWith<IllegalArgumentException> { ExerciseRoute(listOf(location1, location2)) }
     }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt
index 6adb8bb..73af184 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt
@@ -62,7 +62,7 @@
                             )
                         ),
                     exerciseRoute =
-                        ExerciseRoute.Data(
+                        ExerciseRoute(
                             route =
                                 listOf(
                                     ExerciseRoute.Location(
@@ -103,7 +103,7 @@
                             )
                         ),
                     exerciseRoute =
-                        ExerciseRoute.Data(
+                        ExerciseRoute(
                             route =
                                 listOf(
                                     ExerciseRoute.Location(
@@ -131,7 +131,7 @@
                 exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_EXERCISE_CLASS,
                 title = "title",
                 notes = "notes",
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
     }
@@ -181,7 +181,7 @@
                             segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING
                         )
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
 
@@ -200,7 +200,7 @@
                             segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING
                         )
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
     }
@@ -221,7 +221,7 @@
                             endTime = Instant.ofEpochMilli(1235L),
                         )
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
 
@@ -239,7 +239,7 @@
                             endTime = Instant.ofEpochMilli(1236L),
                         )
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
     }
@@ -254,7 +254,7 @@
                 endZoneOffset = null,
                 exerciseType = EXERCISE_TYPE_BIKING,
                 exerciseRoute =
-                    ExerciseRoute.Data(
+                    ExerciseRoute(
                         route =
                             listOf(
                                 ExerciseRoute.Location(
@@ -275,7 +275,7 @@
                 endZoneOffset = null,
                 exerciseType = EXERCISE_TYPE_BIKING,
                 exerciseRoute =
-                    ExerciseRoute.Data(
+                    ExerciseRoute(
                         route =
                             listOf(
                                 ExerciseRoute.Location(
@@ -311,7 +311,7 @@
                             segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING
                         ),
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
     }
@@ -336,7 +336,7 @@
                             endTime = Instant.ofEpochMilli(1236L),
                         ),
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
     }
@@ -358,7 +358,7 @@
                             segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_PLANK
                         ),
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
     }
@@ -373,7 +373,7 @@
                         endZoneOffset = null,
                         exerciseType = EXERCISE_TYPE_BIKING,
                         exerciseRoute =
-                            ExerciseRoute.Data(
+                            ExerciseRoute(
                                 route =
                                     listOf(
                                         ExerciseRoute.Location(
@@ -384,18 +384,20 @@
                                     )
                             ),
                     )
-                    .exerciseRoute
+                    .exerciseRouteResult
             )
             .isEqualTo(
-                ExerciseRoute.Data(
-                    route =
-                        listOf(
-                            ExerciseRoute.Location(
-                                time = Instant.ofEpochMilli(1235L),
-                                latitude = 34.5,
-                                longitude = -34.5
+                ExerciseRouteResult.Data(
+                    ExerciseRoute(
+                        route =
+                            listOf(
+                                ExerciseRoute.Location(
+                                    time = Instant.ofEpochMilli(1235L),
+                                    latitude = 34.5,
+                                    longitude = -34.5
+                                )
                             )
-                        )
+                    )
                 )
             )
         assertThat(
@@ -405,10 +407,10 @@
                         endTime = Instant.ofEpochMilli(1236L),
                         endZoneOffset = null,
                         exerciseType = EXERCISE_TYPE_BIKING,
-                        exerciseRouteData = null
+                        exerciseRoute = null
                     )
-                    .exerciseRoute
+                    .exerciseRouteResult
             )
-            .isEqualTo(ExerciseRoute.NoData())
+            .isEqualTo(ExerciseRouteResult.NoData())
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/BatchingMode.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/BatchingMode.kt
index fa9ea16..b61a875 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/BatchingMode.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/BatchingMode.kt
@@ -37,13 +37,15 @@
 
     public companion object {
         /**
-         * Batching mode for receiving [DataType.HEART_RATE_BPM] updates with fast frequency.
+         * Deliver smaller and more frequent batches of [DataType.HEART_RATE_BPM] when the device is
+         * not interactive (e.g. screen is off).
          *
-         * Note: This mode will cause significantly increased power consumption compared to the
-         * default batching mode, while still being more power efficient than streaming when in
-         * non-interactive state. The exact power/performance tradeoff of this mode is device
-         * implementation dependent and batched updates may be aligned with other wake ups but
-         * target five second updates.
+         * This setting significantly increases power consumption, and is intended to be used by
+         * apps which need to send data to a separate device (e.g. a connected phone or TV) for
+         * real-time visualisation. It has no effect if the device is interactive.
+         *
+         * The exact power/performance tradeoff of this mode is device implementation dependent and
+         * batched updates may be aligned with other wake ups but target five second updates.
          */
         @JvmField public val HEART_RATE_5_SECONDS: BatchingMode = BatchingMode(1)
 
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseCapabilitiesTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseCapabilitiesTest.kt
index 4a43798..2d2bf7e 100644
--- a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseCapabilitiesTest.kt
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseCapabilitiesTest.kt
@@ -31,8 +31,8 @@
             EXERCISE_CAPABILITIES.getExerciseTypeCapabilities(
                 ExerciseType.WALKING
             ).supportedDataTypes
-        ).isEqualTo(
-            ImmutableSet.of(DataType.STEPS)
+        ).containsExactly(
+            DataType.STEPS
         )
     }
 
@@ -93,6 +93,9 @@
         assertThat(capabilities.autoPauseAndResumeEnabledExercises).containsExactlyElementsIn(
             EXERCISE_CAPABILITIES.autoPauseAndResumeEnabledExercises
         )
+        assertThat(capabilities.supportedBatchingModeOverrides).containsExactlyElementsIn(
+            EXERCISE_CAPABILITIES.supportedBatchingModeOverrides
+        )
     }
 
     @Test
@@ -106,6 +109,9 @@
         assertThat(emptyCapabilities.autoPauseAndResumeEnabledExercises).containsExactlyElementsIn(
             roundTripEmptyCapabilities.autoPauseAndResumeEnabledExercises
         )
+        assertThat(emptyCapabilities.supportedBatchingModeOverrides).containsExactlyElementsIn(
+            roundTripEmptyCapabilities.supportedBatchingModeOverrides
+        )
     }
 
     companion object {
@@ -153,6 +159,7 @@
             )
 
         private val EXERCISE_CAPABILITIES: ExerciseCapabilities =
-            ExerciseCapabilities(EXERCISE_TYPE_TO_EXERCISE_CAPABILITIES_MAPPING)
+            ExerciseCapabilities(EXERCISE_TYPE_TO_EXERCISE_CAPABILITIES_MAPPING,
+                ImmutableSet.of(BatchingMode.HEART_RATE_5_SECONDS))
     }
 }
diff --git a/hilt/hilt-compiler/build.gradle b/hilt/hilt-compiler/build.gradle
index 2418bf7..aec41ba 100644
--- a/hilt/hilt-compiler/build.gradle
+++ b/hilt/hilt-compiler/build.gradle
@@ -39,7 +39,7 @@
     testImplementation(project(":hilt:hilt-common"))
     testImplementation(project(":annotation:annotation"))
     testImplementation(libs.junit)
-    testImplementation(project(":kruth:kruth"))
+    testImplementation(libs.truth)
     testImplementation(project(":room:room-compiler-processing-testing"))
     testImplementation(libs.hiltCore)
     testImplementationAarAsJar(project(":hilt:hilt-work"))
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/AGPExtensions.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/AGPExtensions.kt
index 6d6a8e6..520e21d7 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/AGPExtensions.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/AGPExtensions.kt
@@ -20,6 +20,8 @@
 import java.io.File
 import java.util.Locale
 import org.gradle.api.Project
+import org.gradle.api.file.Directory
+import org.gradle.api.provider.Provider
 
 internal fun Variant.taskName(baseName: String) =
     "$baseName${name.replaceFirstChar(Char::titlecase)}"
@@ -27,9 +29,8 @@
 internal fun Project.taskWorkingDir(
     variant: Variant,
     baseName: String
-): File {
-    val inspectionDir = File(project.buildDir, "androidx_inspection")
-    return File(File(inspectionDir, baseName), variant.name)
+): Provider<Directory> {
+    return layout.buildDirectory.dir("androidx_inspection/$baseName/${variant.name}")
 }
 
 // Functions below will be removed once registerGenerateProguardDetectionFileTask is migrated
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
index 7d95e6b..e8edc46 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
@@ -128,7 +128,8 @@
 ): TaskProvider<Copy> {
     return tasks.register(variant.taskName("unpackInspectorAAR"), Copy::class.java) {
         it.from(zipTree(variant.artifacts.get(SingleArtifact.AAR)))
-        it.destinationDir = taskWorkingDir(variant, "unpackedInspectorAAR")
+        // Remove .get().asFile once https://github.com/gradle/gradle/issues/25824 is fixed
+        it.destinationDir = taskWorkingDir(variant, "unpackedInspectorAAR").get().asFile
     }
 }
 
@@ -139,15 +140,14 @@
     jar: TaskProvider<out Jar>
 ): TaskProvider<Zip> {
     val name = jarName ?: "${project.name}.jar"
-    val out = File(taskWorkingDir(variant, "dexedInspector"), name)
+    val output = taskWorkingDir(variant, "dexedInspector").map { it.file(name) }
 
     val dex = tasks.register(variant.taskName("dexInspector"), DexInspectorTask::class.java) {
         it.minSdkVersion = extension.defaultConfig.minSdk!!
         it.setD8(extension.sdkDirectory, extension.buildToolsVersion)
         it.setAndroidJar(extension.sdkDirectory, extension.compileSdkVersion!!)
         it.jars.from(jar.get().archiveFile)
-        it.outputFile.set(out)
-        @Suppress("UnstableApiUsage")
+        it.outputFile.set(output)
         it.compileClasspath.from(
             variant.compileConfiguration.incoming.artifactView {
                 it.attributes {
@@ -163,11 +163,10 @@
 
     return tasks.register(variant.taskName("assembleInspectorJar"), Zip::class.java) {
         it.from(zipTree(jar.map { it.archiveFile }))
-        it.from(zipTree(out))
+        it.from(dex.map { zipTree(it.outputFile) })
         it.exclude("**/*.class")
         it.archiveFileName.set(name)
         it.destinationDirectory.set(taskWorkingDir(variant, "assembleInspectorJar"))
-        it.dependsOn(dex)
         it.includeEmptyDirs = false
     }
 }
diff --git a/libraryversions.toml b/libraryversions.toml
index 0e4ded9..19c0a44 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -59,7 +59,7 @@
 EXIFINTERFACE = "1.4.0-alpha01"
 FRAGMENT = "1.7.0-alpha02"
 FUTURES = "1.2.0-alpha02"
-GLANCE = "1.0.0-rc01"
+GLANCE = "1.1.0-alpha01"
 GLANCE_PREVIEW = "1.0.0-alpha06"
 GLANCE_TEMPLATE = "1.0.0-alpha06"
 GLANCE_WEAR_TILES = "1.0.0-alpha06"
diff --git a/privacysandbox/ads/ads-adservices-java/api/current.txt b/privacysandbox/ads/ads-adservices-java/api/current.txt
index 26eea8b..18004f9 100644
--- a/privacysandbox/ads/ads-adservices-java/api/current.txt
+++ b/privacysandbox/ads/ads-adservices-java/api/current.txt
@@ -64,6 +64,7 @@
     method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
diff --git a/privacysandbox/ads/ads-adservices-java/api/restricted_current.txt b/privacysandbox/ads/ads-adservices-java/api/restricted_current.txt
index 26eea8b..18004f9 100644
--- a/privacysandbox/ads/ads-adservices-java/api/restricted_current.txt
+++ b/privacysandbox/ads/ads-adservices-java/api/restricted_current.txt
@@ -64,6 +64,7 @@
     method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
index 6a657ad4..7bc4ea9 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
@@ -26,6 +26,7 @@
 import androidx.privacysandbox.ads.adservices.java.endtoend.TestUtil;
 import androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures;
 import androidx.privacysandbox.ads.adservices.measurement.DeletionRequest;
+import androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest;
 import androidx.privacysandbox.ads.adservices.measurement.WebSourceParams;
 import androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest;
 import androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams;
@@ -107,6 +108,19 @@
     }
 
     @Test
+    public void testRegisterAppSources_NoServerSetup_NoErrors() throws Exception {
+        // Skip the test if SDK extension 5 is not present.
+        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
+
+        SourceRegistrationRequest request =
+                new SourceRegistrationRequest.Builder(
+                        Collections.singletonList(SOURCE_REGISTRATION_URI))
+                        .build();
+        assertThat(mMeasurementManager.registerSourceAsync(request).get())
+                .isNotNull();
+    }
+
+    @Test
     public void testRegisterTrigger_NoServerSetup_NoErrors() throws Exception {
         // Skip the test if SDK extension 5 is not present.
         Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
index 6e1752b..0bec6eb 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
@@ -24,8 +24,10 @@
 import android.os.ext.SdkExtensions
 import android.view.InputEvent
 import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
 import androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion.from
 import androidx.privacysandbox.ads.adservices.measurement.DeletionRequest
+import androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest
 import androidx.privacysandbox.ads.adservices.measurement.WebSourceParams
 import androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest
 import androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams
@@ -36,13 +38,23 @@
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.Executor
 import kotlin.test.assertNotEquals
+import kotlin.test.assertTrue
+import kotlin.test.fail
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
 import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.atMost
 import org.mockito.Mockito.doAnswer
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.spy
@@ -278,6 +290,112 @@
         assertThat(result.get() == state)
     }
 
+    @ExperimentalFeatures.RegisterSourceOptIn
+    @Test
+    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+    fun testRegisterSourceAsync_allSuccess() {
+        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+        val inputEvent = mock(InputEvent::class.java)
+        val measurementManager = mockMeasurementManager(mContext)
+        val managerCompat = from(mContext)
+        val successCallback = { args: InvocationOnMock ->
+            assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
+            val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+            receiver.onResult(Object())
+            null
+        }
+        doAnswer(successCallback).`when`(measurementManager)
+            .registerSource(any(), any(), any(), any())
+
+        // Actually invoke the compat code.
+        val request = SourceRegistrationRequest.Builder(listOf(uri1, uri2))
+            .setInputEvent(inputEvent)
+            .build()
+        managerCompat!!.registerSourceAsync(request).get()
+
+        // Verify that the compat code was invoked correctly.
+        verify(measurementManager).registerSource(
+            eq(uri1),
+            eq(inputEvent),
+            any(Executor::class.java),
+            any())
+        verify(measurementManager).registerSource(
+            eq(uri2),
+            eq(inputEvent),
+            any(Executor::class.java),
+            any())
+    }
+
+    @ExperimentalFeatures.RegisterSourceOptIn
+    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+    @Test
+    fun testRegisterSource_15thOf20Fails_atLeast15thExecutes() {
+        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+        val measurementManager = mockMeasurementManager(mContext)
+        val mockInputEvent = mock(InputEvent::class.java)
+        val managerCompat = from(mContext)
+        val successCallback = { args: InvocationOnMock ->
+            val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+            receiver.onResult(Object())
+            null
+        }
+
+        val errorMessage = "some error occurred"
+        val errorCallback = { args: InvocationOnMock ->
+            val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+            receiver.onError(IllegalArgumentException(errorMessage))
+            null
+        }
+
+        val uris = (0..20).map { i ->
+            val uri = Uri.parse("www.uri$i.com")
+            if (i == 15) {
+                doAnswer(errorCallback).`when`(measurementManager)
+                    .registerSource(eq(uri), any(), any(), any())
+            } else {
+                doAnswer(successCallback).`when`(measurementManager)
+                    .registerSource(eq(uri), any(), any(), any())
+            }
+            uri
+        }.toList()
+
+        val request = SourceRegistrationRequest(uris, mockInputEvent)
+
+        // Actually invoke the compat code.
+        runBlocking {
+            try {
+                withContext(Dispatchers.Main) {
+                    managerCompat!!.registerSourceAsync(request).get()
+                }
+                fail("Expected failure.")
+            } catch (e: ExecutionException) {
+                assertTrue(e.cause!! is IllegalArgumentException)
+                assertThat(e.cause!!.message).isEqualTo(errorMessage)
+            }
+        }
+
+        // Verify that the compat code was invoked correctly.
+        // registerSource gets called 1-20 times. We cannot predict the exact number because
+        // uri15 would crash asynchronously. Other uris may succeed and those threads on default
+        // dispatcher won't crash.
+        verify(measurementManager, atLeastOnce()).registerSource(
+            any(),
+            eq(mockInputEvent),
+            any(),
+            any()
+        )
+        verify(measurementManager, atMost(20)).registerSource(
+            any(),
+            eq(mockInputEvent),
+            any(),
+            any()
+        )
+    }
+
     @SdkSuppress(minSdkVersion = 30)
     @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     companion object {
diff --git a/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt b/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt
index b04eb21..e6c5487 100644
--- a/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt
@@ -22,10 +22,12 @@
 import android.view.InputEvent
 import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresPermission
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
 import androidx.privacysandbox.ads.adservices.java.internal.asListenableFuture
 import androidx.privacysandbox.ads.adservices.measurement.DeletionRequest
 import androidx.privacysandbox.ads.adservices.measurement.MeasurementManager
 import androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion.obtain
+import androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest
 import androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest
 import androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest
 import com.google.common.util.concurrent.ListenableFuture
@@ -75,6 +77,19 @@
     abstract fun registerTriggerAsync(trigger: Uri): ListenableFuture<Unit>
 
     /**
+     * Register attribution sources(click or view). This API will not process any redirects, all
+     * registration URLs should be supplied with the request.
+     *
+     * @param request source registration request
+     */
+    @ExperimentalFeatures.RegisterSourceOptIn
+    @SuppressWarnings("MissingNullability")
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
+    abstract fun registerSourceAsync(
+        request: SourceRegistrationRequest
+    ): ListenableFuture<Unit>
+
+    /**
      * Register an attribution source(click or view) from web context. This API will not process any
      * redirects, all registration URLs should be supplied with the request. At least one of
      * appDestination or webDestination parameters are required to be provided.
@@ -135,6 +150,17 @@
         }
 
         @DoNotInline
+        @ExperimentalFeatures.RegisterSourceOptIn
+        @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
+        override fun registerSourceAsync(
+            request: SourceRegistrationRequest
+        ): ListenableFuture<Unit> {
+            return CoroutineScope(Dispatchers.Default).async {
+                mMeasurementManager.registerSource(request)
+            }.asListenableFuture()
+        }
+
+        @DoNotInline
         @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
         override fun registerTriggerAsync(trigger: Uri): ListenableFuture<Unit> {
             return CoroutineScope(Dispatchers.Default).async {
diff --git a/privacysandbox/ads/ads-adservices/api/current.txt b/privacysandbox/ads/ads-adservices/api/current.txt
index 839dec6..6a4905b 100644
--- a/privacysandbox/ads/ads-adservices/api/current.txt
+++ b/privacysandbox/ads/ads-adservices/api/current.txt
@@ -126,6 +126,12 @@
     property public final String identifier;
   }
 
+  public sealed interface ExperimentalFeatures {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.RegisterSourceOptIn {
+  }
+
 }
 
 package androidx.privacysandbox.ads.adservices.customaudience {
@@ -249,6 +255,7 @@
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
     method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract suspend Object? registerSource(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -261,6 +268,20 @@
     method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
   }
 
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public final class SourceRegistrationRequest {
+    ctor public SourceRegistrationRequest(java.util.List<? extends android.net.Uri> registrationUris, optional android.view.InputEvent? inputEvent);
+    method public android.view.InputEvent? getInputEvent();
+    method public java.util.List<android.net.Uri> getRegistrationUris();
+    property public final android.view.InputEvent? inputEvent;
+    property public final java.util.List<android.net.Uri> registrationUris;
+  }
+
+  public static final class SourceRegistrationRequest.Builder {
+    ctor public SourceRegistrationRequest.Builder(java.util.List<? extends android.net.Uri> registrationUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+  }
+
   @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
     ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
     method public boolean getDebugKeyAllowed();
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_current.txt b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
index 839dec6..6a4905b 100644
--- a/privacysandbox/ads/ads-adservices/api/restricted_current.txt
+++ b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
@@ -126,6 +126,12 @@
     property public final String identifier;
   }
 
+  public sealed interface ExperimentalFeatures {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.RegisterSourceOptIn {
+  }
+
 }
 
 package androidx.privacysandbox.ads.adservices.customaudience {
@@ -249,6 +255,7 @@
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
     method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract suspend Object? registerSource(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -261,6 +268,20 @@
     method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
   }
 
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public final class SourceRegistrationRequest {
+    ctor public SourceRegistrationRequest(java.util.List<? extends android.net.Uri> registrationUris, optional android.view.InputEvent? inputEvent);
+    method public android.view.InputEvent? getInputEvent();
+    method public java.util.List<android.net.Uri> getRegistrationUris();
+    property public final android.view.InputEvent? inputEvent;
+    property public final java.util.List<android.net.Uri> registrationUris;
+  }
+
+  public static final class SourceRegistrationRequest.Builder {
+    ctor public SourceRegistrationRequest.Builder(java.util.List<? extends android.net.Uri> registrationUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+  }
+
   @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
     ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
     method public boolean getDebugKeyAllowed();
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
index 191e2d8..1e0a826 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
@@ -23,13 +23,16 @@
 import android.os.ext.SdkExtensions
 import android.view.InputEvent
 import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
 import androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion.obtain
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import androidx.testutils.fail
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
+import kotlin.IllegalArgumentException
 import kotlinx.coroutines.runBlocking
 import org.junit.Assume
 import org.junit.Before
@@ -37,9 +40,12 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mockito.doAnswer
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.invocation.InvocationOnMock
@@ -211,6 +217,105 @@
         assertThat(!actualRequest.sourceParams[0].isDebugKeyAllowed)
     }
 
+    @ExperimentalFeatures.RegisterSourceOptIn
+    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+    @Test
+    fun testRegisterSource_allSuccess() {
+        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+        val measurementManager = mockMeasurementManager(mContext)
+        val mockInputEvent = mock(InputEvent::class.java)
+        val managerCompat = obtain(mContext)
+        val successCallback = { args: InvocationOnMock ->
+            val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+            receiver.onResult(Object())
+            null
+        }
+        doAnswer(successCallback).`when`(measurementManager)
+            .registerSource(any(), any(), any(), any())
+
+        val request = SourceRegistrationRequest(listOf(uri1, uri2), mockInputEvent)
+
+        // Actually invoke the compat code.
+        runBlocking {
+            managerCompat!!.registerSource(request)
+        }
+
+        // Verify that the compat code was invoked correctly.
+        verify(measurementManager, times(2)).registerSource(
+            any(),
+            eq(mockInputEvent),
+            any(),
+            any()
+        )
+    }
+
+    @ExperimentalFeatures.RegisterSourceOptIn
+    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+    @Test
+    fun testRegisterSource_15thOf20Fails_remaining5DoNotExecute() {
+        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+        val measurementManager = mockMeasurementManager(mContext)
+        val mockInputEvent = mock(InputEvent::class.java)
+        val managerCompat = obtain(mContext)
+        val successCallback = { args: InvocationOnMock ->
+            val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+            receiver.onResult(Object())
+            null
+        }
+
+        val errorMessage = "some error occurred"
+        val errorCallback = { args: InvocationOnMock ->
+            val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+            receiver.onError(IllegalArgumentException(errorMessage))
+            null
+        }
+        val uris = (0..20).map { i ->
+            val uri = Uri.parse("www.uri$i.com")
+            if (i == 15) {
+                doAnswer(errorCallback).`when`(measurementManager)
+                    .registerSource(eq(uri), any(), any(), any())
+            } else {
+                doAnswer(successCallback).`when`(measurementManager)
+                    .registerSource(eq(uri), any(), any(), any())
+            }
+            uri
+        }.toList()
+
+        val request = SourceRegistrationRequest(uris, mockInputEvent)
+
+        // Actually invoke the compat code.
+        runBlocking {
+            try {
+                managerCompat!!.registerSource(request)
+                fail("Expected failure.")
+            } catch (e: IllegalArgumentException) {
+                assertThat(e.message).isEqualTo(errorMessage)
+            }
+        }
+
+        // Verify that the compat code was invoked correctly.
+        (0..15).forEach { i ->
+            verify(measurementManager).registerSource(
+                eq(Uri.parse("www.uri$i.com")),
+                eq(mockInputEvent),
+                any(),
+                any()
+            )
+        }
+        (16..20).forEach { i ->
+            verify(measurementManager, never()).registerSource(
+                eq(Uri.parse("www.uri$i.com")),
+                eq(mockInputEvent),
+                any(),
+                any()
+            )
+        }
+    }
+
     @Test
     @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testRegisterWebTrigger() {
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequestTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequestTest.kt
new file mode 100644
index 0000000..cff885a
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequestTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 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.privacysandbox.ads.adservices.measurement
+
+import android.net.Uri
+import android.view.InputEvent
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+@ExperimentalFeatures.RegisterSourceOptIn
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 33)
+class SourceRegistrationRequestTest {
+    @Test
+    fun testToString() {
+        val result = "AppSourcesRegistrationRequest { RegistrationUris=" +
+            "[[www.abc.com, www.xyz.com]], InputEvent=null }"
+
+        val uri1 = Uri.parse("www.abc.com")
+        val uri2 = Uri.parse("www.xyz.com")
+        val params = listOf(uri1, uri2)
+        val request = SourceRegistrationRequest.Builder(params).build()
+        Truth.assertThat(request.toString()).isEqualTo(result)
+    }
+
+    @Test
+    fun testEquals() {
+        val uri1 = Uri.parse("www.abc.com")
+        val uri2 = Uri.parse("www.xyz.com")
+        val params = listOf(uri1, uri2)
+        val inputEvent = mock(InputEvent::class.java)
+        val request1 = SourceRegistrationRequest.Builder(params)
+            .setInputEvent(inputEvent)
+            .build()
+        val request2 = SourceRegistrationRequest(params, inputEvent)
+        val request3 = SourceRegistrationRequest.Builder(params).build()
+
+        Truth.assertThat(request1 == request2).isTrue()
+        Truth.assertThat(request1 != request3).isTrue()
+    }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequestTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequestTest.kt
index e76c23e..70cd0c4 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequestTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequestTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import androidx.testutils.assertThrows
 import com.google.common.truth.Truth
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -41,4 +42,30 @@
         // Verify equality.
         Truth.assertThat(request == request2).isTrue()
     }
+
+    @Test
+    fun testToString_emptySdkName() {
+        val result = "GetTopicsRequest: adsSdkName=, shouldRecordObservation=true"
+        val request = GetTopicsRequest("", true)
+        Truth.assertThat(request.toString()).isEqualTo(result)
+
+        // Verify Builder.
+        val request2 = GetTopicsRequest.Builder()
+            .setShouldRecordObservation(true)
+            .build()
+        Truth.assertThat(request.toString()).isEqualTo(result)
+
+        // Verify equality.
+        Truth.assertThat(request == request2).isTrue()
+    }
+
+    @Test
+    fun testBuilder_setEmptyAdsSdkName_throwsError() {
+        assertThrows(IllegalStateException::class.java) {
+            GetTopicsRequest.Builder()
+                .setAdsSdkName("")
+                .setShouldRecordObservation(true)
+                .build()
+        }
+    }
 }
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/ExperimentalFeatures.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/ExperimentalFeatures.kt
new file mode 100644
index 0000000..e1935b3
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/ExperimentalFeatures.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 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.privacysandbox.ads.adservices.common
+
+/**
+ * Contains AdServices experimental feature opt-in anntations.
+ */
+sealed interface ExperimentalFeatures {
+    /**
+     * Clients should use it when they want to use [MeasurementManager#registerSource] API.
+     */
+    @RequiresOptIn("This API is experimental.", RequiresOptIn.Level.WARNING)
+    annotation class RegisterSourceOptIn
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
index e316ab0..de62bd2 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
@@ -27,7 +27,10 @@
 import androidx.annotation.RequiresExtension
 import androidx.annotation.RequiresPermission
 import androidx.core.os.asOutcomeReceiver
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
 import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.suspendCancellableCoroutine
 
 /**
@@ -82,6 +85,16 @@
     abstract suspend fun registerWebTrigger(request: WebTriggerRegistrationRequest)
 
     /**
+     * Register an attribution source(click or view) context. This API will not process any
+     * redirects, all registration URLs should be supplied with the request.
+     *
+     * @param request source registration request
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    @ExperimentalFeatures.RegisterSourceOptIn
+    abstract suspend fun registerSource(request: SourceRegistrationRequest)
+
+    /**
      * Get Measurement API status.
      *
      * The call returns an integer value (see [MEASUREMENT_API_STATE_DISABLED] and
@@ -160,6 +173,26 @@
             }
         }
 
+        @DoNotInline
+        @ExperimentalFeatures.RegisterSourceOptIn
+        @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+        override suspend fun registerSource(
+            request: SourceRegistrationRequest
+        ): Unit = coroutineScope {
+            request.registrationUris.forEach { uri ->
+                launch {
+                    suspendCancellableCoroutine<Any> { continuation ->
+                        mMeasurementManager.registerSource(
+                            uri,
+                            request.inputEvent,
+                            Runnable::run,
+                            continuation.asOutcomeReceiver()
+                        )
+                    }
+                }
+            }
+        }
+
         private fun convertWebSourceRequest(
             request: WebSourceRegistrationRequest
         ): android.adservices.measurement.WebSourceRegistrationRequest {
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequest.kt
new file mode 100644
index 0000000..7bda8bf
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 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.privacysandbox.ads.adservices.measurement
+
+import android.net.Uri
+import android.view.InputEvent
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
+
+/**
+ * Class to hold input to measurement source registration calls from web context.
+ *
+ * @param registrationUris [List] of Registration [Uri]s to fetch sources.
+ * @param inputEvent User Interaction [InputEvent] used by the AttributionReporting API to
+ * distinguish clicks from views.
+ */
+@ExperimentalFeatures.RegisterSourceOptIn
+class SourceRegistrationRequest constructor(
+    val registrationUris: List<Uri>,
+    val inputEvent: InputEvent? = null
+    ) {
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is SourceRegistrationRequest) return false
+        return this.registrationUris == other.registrationUris &&
+            this.inputEvent == other.inputEvent
+    }
+
+    override fun hashCode(): Int {
+        var hash = registrationUris.hashCode()
+        if (inputEvent != null) {
+            hash = 31 * hash + inputEvent.hashCode()
+        }
+        return hash
+    }
+
+    override fun toString(): String {
+        val vals = "RegistrationUris=[$registrationUris], InputEvent=$inputEvent"
+        return "AppSourcesRegistrationRequest { $vals }"
+    }
+
+    /**
+     * Builder for [SourceRegistrationRequest].
+     *
+     * @param registrationUris source registration request [Uri]
+     */
+    class Builder(
+        private val registrationUris: List<Uri>
+    ) {
+        private var inputEvent: InputEvent? = null
+
+        /**
+         * Setter for input event.
+         *
+         * @param inputEvent User Interaction InputEvent used by the AttributionReporting API to
+         *     distinguish clicks from views.
+         * @return builder
+         */
+        fun setInputEvent(inputEvent: InputEvent): Builder = apply {
+            this.inputEvent = inputEvent
+        }
+
+        /** Pre-validates parameters and builds [SourceRegistrationRequest]. */
+        fun build(): SourceRegistrationRequest {
+            return SourceRegistrationRequest(
+                registrationUris,
+                inputEvent
+            )
+        }
+    }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequest.kt
index d8f4a81..6dede0c 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequest.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequest.kt
@@ -64,7 +64,10 @@
          *
          * @param adsSdkName the Ads Sdk Name.
          */
-        fun setAdsSdkName(adsSdkName: String): Builder = apply { this.adsSdkName = adsSdkName }
+        fun setAdsSdkName(adsSdkName: String): Builder = apply {
+            check(adsSdkName.isNotEmpty()) { "adsSdkName must be set" }
+            this.adsSdkName = adsSdkName
+        }
 
         /**
          * Set the Record Observation.
@@ -80,7 +83,6 @@
 
         /** Builds a [GetTopicsRequest] instance. */
         fun build(): GetTopicsRequest {
-            check(adsSdkName.isNotEmpty()) { "adsSdkName must be set" }
             return GetTopicsRequest(adsSdkName, shouldRecordObservation)
         }
     }
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
index a7ef64d..5753602 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
@@ -20,6 +20,7 @@
 import androidx.collection.SparseArrayCompat
 import androidx.lifecycle.LiveData
 import androidx.room.Dao
+import androidx.room.Delete
 import androidx.room.Insert
 import androidx.room.MapInfo
 import androidx.room.Query
@@ -32,6 +33,9 @@
 import androidx.room.integration.kotlintestapp.vo.Artist
 import androidx.room.integration.kotlintestapp.vo.Image
 import androidx.room.integration.kotlintestapp.vo.ImageFormat
+import androidx.room.integration.kotlintestapp.vo.Playlist
+import androidx.room.integration.kotlintestapp.vo.PlaylistSongXRef
+import androidx.room.integration.kotlintestapp.vo.PlaylistWithSongs
 import androidx.room.integration.kotlintestapp.vo.ReleasedAlbum
 import androidx.room.integration.kotlintestapp.vo.Song
 import androidx.sqlite.db.SupportSQLiteQuery
@@ -41,6 +45,7 @@
 import io.reactivex.Flowable
 import java.nio.ByteBuffer
 import java.util.Date
+import kotlinx.coroutines.flow.Flow
 
 @JvmDefaultWithCompatibility
 @Dao
@@ -53,6 +58,16 @@
 
     @Insert
     fun addAlbums(vararg albums: Album)
+
+    @Insert
+    fun addPlaylists(vararg playlists: Playlist)
+
+    @Insert
+    fun addPlaylistSongRelations(vararg relations: PlaylistSongXRef)
+
+    @Delete
+    fun removePlaylistSongRelations(vararg relations: PlaylistSongXRef)
+
     @Insert
     fun addImages(vararg images: Image)
 
@@ -355,4 +370,8 @@
     @MapInfo(keyColumn = "mImageYear", valueColumn = "mTitle")
     @RewriteQueriesToDropUnusedColumns
     fun getNestedMapWithMapInfoKeyAndValue(): Map<Long, Map<Artist, Map<Album, List<String>>>>
+
+    @Transaction
+    @Query("SELECT * FROM Playlist WHERE mPlaylistId = :id")
+    fun getPlaylistsWithSongsFlow(id: Int): Flow<PlaylistWithSongs>
 }
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt
index 4a23ca5..02d20ce 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt
@@ -18,6 +18,10 @@
 
 import androidx.kruth.assertThat
 import androidx.room.integration.kotlintestapp.vo.Book
+import androidx.room.integration.kotlintestapp.vo.Playlist
+import androidx.room.integration.kotlintestapp.vo.PlaylistSongXRef
+import androidx.room.integration.kotlintestapp.vo.PlaylistWithSongs
+import androidx.room.integration.kotlintestapp.vo.Song
 import androidx.room.withTransaction
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -30,7 +34,6 @@
 import kotlinx.coroutines.cancelAndJoin
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.flow.buffer
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.produceIn
 import kotlinx.coroutines.flow.take
@@ -318,4 +321,73 @@
 
         job.cancelAndJoin()
     }
+
+    @Test
+    fun playlistSongs_async_update(): Unit = runBlocking {
+        val musicDao = database.musicDao()
+        val song1 = Song(
+            1,
+            "I Know Places",
+            "Taylor Swift",
+            "1989",
+            195,
+            2014
+        );
+        val song2 = Song(
+            2,
+            "Blank Space",
+            "Taylor Swift",
+            "1989",
+            241,
+            2014
+        )
+        musicDao.addSongs(song1, song2)
+        musicDao.addPlaylists(
+            Playlist(1),
+            Playlist(2)
+        )
+        musicDao.addPlaylistSongRelations(PlaylistSongXRef(1, 1))
+
+        val latches = Array(3) { CountDownLatch(1) }
+        val results = mutableListOf<PlaylistWithSongs>()
+        var collectCall = 0
+        val job = async(Dispatchers.IO) {
+            musicDao.getPlaylistsWithSongsFlow(1).collect {
+                if (collectCall >= latches.size) {
+                    fail("Should have only collected 3 results.")
+                }
+                results.add(it)
+                latches[collectCall].countDown()
+                collectCall++
+            }
+        }
+
+        latches[0].await()
+        assertThat(results.size).isEqualTo(1)
+        results[0].let { playlist ->
+            assertThat(playlist.songs.size).isEqualTo(1)
+            assertThat(playlist.songs[0]).isEqualTo(song1)
+        }
+
+        musicDao.addPlaylistSongRelations(PlaylistSongXRef(1, 2))
+
+        latches[1].await()
+        assertThat(results.size).isEqualTo(2)
+        results[1].let { playlist ->
+            assertThat(playlist.songs.size).isEqualTo(2)
+            assertThat(playlist.songs[0]).isEqualTo(song1)
+            assertThat(playlist.songs[1]).isEqualTo(song2)
+        }
+
+        musicDao.removePlaylistSongRelations(PlaylistSongXRef(1, 2))
+
+        latches[2].await()
+        assertThat(results.size).isEqualTo(3)
+        results[2].let { playlist ->
+            assertThat(playlist.songs.size).isEqualTo(1)
+            assertThat(playlist.songs[0]).isEqualTo(song1)
+        }
+
+        job.cancelAndJoin()
+    }
 }
diff --git a/room/room-compiler-processing-testing/build.gradle b/room/room-compiler-processing-testing/build.gradle
index e789fcd..5b2da1b 100644
--- a/room/room-compiler-processing-testing/build.gradle
+++ b/room/room-compiler-processing-testing/build.gradle
@@ -30,7 +30,6 @@
     implementation(libs.kotlinStdlibJdk8) // KSP defines older version as dependency, force update.
     implementation(libs.ksp)
     implementation(libs.googleCompileTesting)
-    implementation(project(":kruth:kruth"))
     // specify these to match the kotlin compiler version in AndroidX rather than what KSP or KCT
     // uses
     implementation(libs.kotlinCompilerEmbeddable)
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
index e38371a..6642422 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
@@ -16,16 +16,18 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.FailureMetadata
-import androidx.kruth.StringSubject
-import androidx.kruth.Subject
-import androidx.kruth.assertAbout
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.ExperimentalProcessingApi
 import androidx.room.compiler.processing.SyntheticJavacProcessor
 import androidx.room.compiler.processing.SyntheticProcessor
 import androidx.room.compiler.processing.util.compiler.TestCompilationResult
 import androidx.room.compiler.processing.util.runner.CompilationTestRunner
+import com.google.common.truth.Fact.fact
+import com.google.common.truth.Fact.simpleFact
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.StringSubject
+import com.google.common.truth.Subject
+import com.google.common.truth.Subject.Factory
+import com.google.common.truth.Truth
 import com.google.testing.compile.Compilation
 import java.util.regex.Pattern
 import javax.tools.Diagnostic
@@ -124,9 +126,8 @@
 class CompilationResultSubject internal constructor(
     failureMetadata: FailureMetadata,
     val compilationResult: CompilationResult,
-) : Subject<CompilationResult>(
-    actual = compilationResult,
-    metadata = failureMetadata,
+) : Subject<CompilationResultSubject, CompilationResult>(
+    failureMetadata, compilationResult
 ) {
     /**
      * set to true if any assertion on the subject requires it to fail (e.g. looking for errors)
@@ -149,7 +150,9 @@
     fun hasRawOutputContaining(expected: String) = apply {
         val found = compilationResult.rawOutput().contains(expected)
         if (!found) {
-            failWithActual("Did not find $expected in the output.")
+            failWithActual(
+                simpleFact("Did not find $expected in the output.")
+            )
         }
     }
 
@@ -171,7 +174,9 @@
     private fun hasDiagnosticCount(kind: Diagnostic.Kind, expected: Int) = apply {
         val actual = compilationResult.diagnosticsOfKind(kind).size
         if (actual != expected) {
-            failWithActual("expected $expected $kind messages, found $actual")
+            failWithActual(
+                simpleFact("expected $expected $kind messages, found $actual")
+            )
         }
     }
     /**
@@ -326,7 +331,9 @@
     fun hasError() = apply {
         shouldSucceed = false
         if (compilationResult.diagnosticsOfKind(Diagnostic.Kind.ERROR).isEmpty()) {
-            failWithActual("expected at least one failure message")
+            failWithActual(
+                simpleFact("expected at least one failure message")
+            )
         }
     }
 
@@ -337,9 +344,12 @@
      */
     fun generatedSourceFileWithPath(relativePath: String): StringSubject {
         val match = findGeneratedSource(relativePath)
-            ?: failWithActual("Didn't generate file with path: $relativePath")
-
-        return assertThat(match.contents)
+        if (match == null) {
+            failWithActual(
+                simpleFact("Didn't generate file with path: $relativePath")
+            )
+        }
+        return Truth.assertThat(match!!.contents)
     }
 
     private fun findGeneratedSource(relativePath: String) = compilationResult.generatedSources
@@ -358,14 +368,20 @@
     fun generatedSource(source: Source) = apply {
         val match = compilationResult.generatedSources.firstOrNull {
             it.relativePath == source.relativePath
-        } ?: failWithActual("Didn't generate $source")
+        }
+        if (match == null) {
+            failWithActual(
+                simpleFact("Didn't generate $source")
+            )
+            return@apply
+        }
         val mismatch = source.findMismatch(match)
         if (mismatch != null) {
             failWithActual(
-                "Generated code does not match expected",
-                "mismatch: $mismatch",
-                "expected: ${source.contents}",
-                "actual: ${match.contents}",
+                simpleFact("Generated code does not match expected"),
+                fact("mismatch", mismatch),
+                fact("expected", source.contents),
+                fact("actual", match.contents),
             )
         }
     }
@@ -377,8 +393,10 @@
     internal fun assertCompilationResult() {
         if (compilationResult.successfulCompilation != shouldSucceed) {
             failWithActual(
-                "expected compilation result to be: $shouldSucceed but was " +
-                    "${compilationResult.successfulCompilation}"
+                simpleFact(
+                    "expected compilation result to be: $shouldSucceed but was " +
+                        "${compilationResult.successfulCompilation}"
+                )
             )
         }
     }
@@ -389,7 +407,9 @@
      */
     internal fun assertAllExpectedRoundsAreCompleted() {
         if (compilationResult.processor.expectsAnotherRound()) {
-            failWithActual("Test runner requested another round but that didn't happen")
+            failWithActual(
+                simpleFact("Test runner requested another round but that didn't happen")
+            )
         }
     }
 
@@ -427,7 +447,7 @@
             }
         }
         if (matches.isEmpty()) {
-            failWithActual(buildErrorMessage())
+            failWithActual(simpleFact(buildErrorMessage()))
         }
         return DiagnosticMessagesSubject.assertThat(matches)
     }
@@ -449,7 +469,7 @@
             }
         }
         if (matches.isEmpty()) {
-            failWithActual(buildErrorMessage())
+            failWithActual(simpleFact(buildErrorMessage()))
         }
         return DiagnosticMessagesSubject.assertThat(matches)
     }
@@ -470,10 +490,18 @@
     }
 
     companion object {
+        private val FACTORY =
+            Factory<CompilationResultSubject, CompilationResult> { metadata, actual ->
+                CompilationResultSubject(metadata, actual)
+            }
+
         fun assertThat(
             compilationResult: CompilationResult
-        ): CompilationResultSubject =
-            assertAbout(::CompilationResultSubject).that(compilationResult)
+        ): CompilationResultSubject {
+            return Truth.assertAbout(FACTORY).that(
+                compilationResult
+            )
+        }
     }
 }
 
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessagesSubject.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessagesSubject.kt
index de1f737..213eea0 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessagesSubject.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessagesSubject.kt
@@ -16,9 +16,11 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.FailureMetadata
-import androidx.kruth.Subject
-import androidx.kruth.assertAbout
+import com.google.common.truth.Fact.simpleFact
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.Subject
+import com.google.common.truth.Subject.Factory
+import com.google.common.truth.Truth
 
 /**
  * Truth subject for diagnostic messages
@@ -26,8 +28,8 @@
 class DiagnosticMessagesSubject internal constructor(
     failureMetadata: FailureMetadata,
     private val diagnosticMessages: List<DiagnosticMessage>,
-) : Subject<List<DiagnosticMessage>>(
-    diagnosticMessages, failureMetadata
+) : Subject<DiagnosticMessagesSubject, List<DiagnosticMessage>>(
+    failureMetadata, diagnosticMessages
 ) {
 
     private val lineContents by lazy {
@@ -50,10 +52,15 @@
      * Note that if there are multiple messages, any match will be sufficient.
      */
     fun onLine(lineNumber: Int) = apply {
-        if (locations.none { it.line == lineNumber }) {
+        if (locations.none {
+            it.line == lineNumber
+        }
+        ) {
             failWithActual(
-                "expected line $lineNumber but it was " +
-                    locations.joinToString(",")
+                simpleFact(
+                    "expected line $lineNumber but it was " +
+                        locations.joinToString(",")
+                )
             )
         }
     }
@@ -64,7 +71,7 @@
     fun hasCount(expected: Int) = apply {
         if (diagnosticMessages.size != expected) {
             failWithActual(
-                "expected $expected messages, found ${diagnosticMessages.size}"
+                simpleFact("expected $expected messages, found ${diagnosticMessages.size}")
             )
         }
     }
@@ -75,7 +82,7 @@
     fun onLineContaining(content: String) = apply {
         if (lineContents.isEmpty()) {
             failWithActual(
-                "Cannot validate line content due to missing location information"
+                simpleFact("Cannot validate line content due to missing location information")
             )
         }
         if (lineContents.none {
@@ -83,8 +90,10 @@
         }
         ) {
             failWithActual(
-                "expected line content with $content but was " +
-                    lineContents.joinToString("\n")
+                simpleFact(
+                    "expected line content with $content but was " +
+                        lineContents.joinToString("\n")
+                )
             )
         }
     }
@@ -96,18 +105,28 @@
     fun onSource(source: Source) = apply {
         if (locations.none { it.source == source }) {
             failWithActual(
-                """
+                simpleFact(
+                    """
                     Expected diagnostic to be on $source but found it on
                     ${locations.joinToString(",")}
-                """.trimIndent()
+                    """.trimIndent()
+                )
             )
         }
     }
 
     companion object {
+        private val FACTORY =
+            Factory<DiagnosticMessagesSubject, List<DiagnosticMessage>> { metadata, actual ->
+                DiagnosticMessagesSubject(metadata, actual)
+            }
+
         fun assertThat(
             diagnosticMessages: List<DiagnosticMessage>
-        ): DiagnosticMessagesSubject =
-            assertAbout(::DiagnosticMessagesSubject).that(diagnosticMessages)
+        ): DiagnosticMessagesSubject {
+            return Truth.assertAbout(FACTORY).that(
+                diagnosticMessages
+            )
+        }
     }
 }
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
index bcef38b..a0145e7 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.processing.ExperimentalProcessingApi
 import androidx.room.compiler.processing.XProcessingEnvConfig
 import androidx.room.compiler.processing.XProcessingEnvironmentTestConfigProvider
@@ -32,6 +30,8 @@
 import androidx.room.compiler.processing.util.runner.KspCompilationTestRunner
 import androidx.room.compiler.processing.util.runner.TestCompilationParameters
 import com.google.common.io.Files
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import com.google.devtools.ksp.processing.SymbolProcessorProvider
 import java.io.File
 import java.util.jar.JarEntry
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
index 959e75e..4e230a0 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
@@ -16,10 +16,10 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.processing.ExperimentalProcessingApi
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.compiler.processing.XRoundEnv
+import com.google.common.truth.Truth
 import kotlin.reflect.KClass
 
 /**
@@ -100,7 +100,7 @@
     }
 
     private fun assertNotDisposed() {
-        assertWithMessage("Cannot use a test invocation after it is disposed.")
+        Truth.assertWithMessage("Cannot use a test invocation after it is disposed.")
             .that(disposed)
             .isFalse()
     }
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticMessageCollectorTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticMessageCollectorTest.kt
index 1c5c650..66b21fa5 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticMessageCollectorTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticMessageCollectorTest.kt
@@ -16,10 +16,10 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.compiler.DiagnosticsMessageCollector
 import androidx.room.compiler.processing.util.compiler.steps.RawDiagnosticMessage
 import androidx.room.compiler.processing.util.compiler.steps.RawDiagnosticMessage.Location
+import com.google.common.truth.Truth.assertThat
 import javax.tools.Diagnostic
 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
 import org.junit.Test
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
index d211727..7829bd4 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.ExperimentalProcessingApi
+import com.google.common.truth.Truth.assertThat
 import javax.tools.Diagnostic
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt
index 7d71f16..ba43888 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt
@@ -16,11 +16,10 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.ExperimentalProcessingApi
 import androidx.room.compiler.processing.XElement
 import androidx.room.compiler.processing.compat.XConverters.toXProcessing
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.JavaFile
 import com.squareup.javapoet.TypeName
 import com.squareup.javapoet.TypeSpec
@@ -339,8 +338,7 @@
             }
         }
 
-        // Kruth doesn't support exceptions yet
-        Truth.assertThat(result.exceptionOrNull())
+        assertThat(result.exceptionOrNull())
             .hasCauseThat()
             .hasMessageThat()
             .contains(
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/LoadFromDefaultEnvironmentConfigurationProviderTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/LoadFromDefaultEnvironmentConfigurationProviderTest.kt
index 773c6cd..40bdb48 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/LoadFromDefaultEnvironmentConfigurationProviderTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/LoadFromDefaultEnvironmentConfigurationProviderTest.kt
@@ -16,7 +16,7 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class LoadFromDefaultEnvironmentConfigurationProviderTest {
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/MultiRoundProcessingTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/MultiRoundProcessingTest.kt
index f05bf87..7df4df1 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/MultiRoundProcessingTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/MultiRoundProcessingTest.kt
@@ -16,7 +16,7 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.JavaFile
 import com.squareup.javapoet.TypeSpec
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/SourceSetTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/SourceSetTest.kt
index 203f195..98fd644 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/SourceSetTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/SourceSetTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.compiler.SourceSet
+import com.google.common.truth.Truth.assertThat
 import org.jetbrains.kotlin.konan.file.File
 import org.junit.Rule
 import org.junit.Test
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestConfigTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestConfigTest.kt
index cde233d..7f731db 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestConfigTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestConfigTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.CompilationTestCapabilities.Config
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class TestConfigTest {
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt
index 10cf032..32fc356 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.ExperimentalProcessingApi
 import androidx.room.compiler.processing.SyntheticJavacProcessor
 import androidx.room.compiler.processing.SyntheticKspProcessor
@@ -29,7 +28,7 @@
 import androidx.room.compiler.processing.util.compiler.TestCompilationArguments
 import androidx.room.compiler.processing.util.compiler.compile
 import androidx.room.compiler.processing.util.compiler.steps.KaptCompilationStep
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import com.google.devtools.ksp.processing.SymbolProcessor
 import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
 import com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -144,8 +143,7 @@
             "c" to "d"
         )
         val handler: (XTestInvocation) -> Unit = {
-            // Kruth MapSubject doesn't support containsAtLeastEntriesIn yet
-            Truth.assertThat(it.processingEnv.options).containsAtLeastEntriesIn(testOptions)
+            assertThat(it.processingEnv.options).containsAtLeastEntriesIn(testOptions)
         }
         runJavaProcessorTest(
             sources = emptyList(),
@@ -425,7 +423,7 @@
             "org.jetbrains.kotlin.kapt3",
             listOf("-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true")
         ).let { options ->
-            assertThat(options).containsExactly("correctErrorTypes" to "true")
+            assertThat(options).containsExactly("correctErrorTypes", "true")
         }
 
         // zero args
@@ -441,7 +439,7 @@
             "org.jetbrains.kotlin.kapt3",
             listOf("-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true", "-verbose")
         ).let { options ->
-            assertThat(options).containsExactly("correctErrorTypes" to "true")
+            assertThat(options).containsExactly("correctErrorTypes", "true")
         }
 
         // illegal format (missing "=")
@@ -476,8 +474,8 @@
             )
         ).let { options ->
             assertThat(options).containsExactly(
-                "correctErrorTypes" to "true",
-                "sources" to "build/kapt/sources"
+                "correctErrorTypes", "true",
+                "sources", "build/kapt/sources"
             )
         }
     }
diff --git a/room/room-compiler-processing/build.gradle b/room/room-compiler-processing/build.gradle
index 0e71dd2..edd335d 100644
--- a/room/room-compiler-processing/build.gradle
+++ b/room/room-compiler-processing/build.gradle
@@ -84,9 +84,9 @@
     testImplementation(libs.jsr250)
     testImplementation(libs.ksp)
     testImplementation(libs.kotlinMetadataJvm)
+    testImplementation(libs.testParameterInjector)
     testImplementation(project(":room:room-compiler-processing-testing"))
     testImplementation(project(":internal-testutils-common"))
-    testImplementation(project(":kruth:kruth"))
 }
 
 tasks.withType(KotlinCompile).configureEach {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
index 13ace16..2012535 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
@@ -51,26 +51,27 @@
     }
 
     override val annotationValues: List<XAnnotationValue> by lazy {
-        // In KSP the annotation members may be represented by constructor parameters in kotlin
-        // source or by abstract methods in java source so we check both.
-        val declarationConstructors = typeElement.let {
-            // We access constructor using declaration since for compatibility with KAPT,
-            // XTypeElement.getConstructors() will return an empty list for annotation classes.
-            check(it is KspTypeElement)
-            it.declaration.getConstructors().map {
-                KspConstructorElement(
-                    env = env,
-                    declaration = it
-                )
+        // Whether the annotation value is being treated as property or abstract method depends on
+        // the actual usage of the annotation. If the annotation is being used on Java source, then
+        // the annotation value will have a corresponding method element, otherwise, it will become
+        // a kotlin property.
+        val typesByName =
+            buildMap {
+                typeElement.getDeclaredMethods()
+                    .filter {
+                        if ((typeElement as KspTypeElement).declaration
+                                .getConstructors()
+                                .single().parameters
+                                .isNotEmpty()) {
+                            it.isKotlinPropertyMethod()
+                        } else {
+                            it.isAbstract()
+                        }
+                    }.forEach {
+                        put(it.name, it.returnType)
+                        put(it.jvmName, it.returnType)
+                    }
             }
-        }
-        val typesByName = if (declarationConstructors.single().parameters.isNotEmpty()) {
-            declarationConstructors.single().parameters.associate { it.name to it.type }
-        } else {
-            typeElement.getDeclaredMethods()
-                .filter { it.isAbstract() }
-                .associate { it.name to it.returnType }
-        }
         // KSAnnotated.arguments isn't guaranteed to have the same ordering as declared in the
         // annotation declaration, so we order it manually using a map from name to index.
         val indexByName = typesByName.keys.mapIndexed { index, name -> name to index }.toMap()
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/codegen/XTypeNameTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/codegen/XTypeNameTest.kt
index e93dfd6..7ba9a46 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/codegen/XTypeNameTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/codegen/XTypeNameTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.compiler.codegen
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.XNullability
+import com.google.common.truth.Truth.assertThat
 import com.squareup.kotlinpoet.INT
 import com.squareup.kotlinpoet.SHORT
 import com.squareup.kotlinpoet.javapoet.JClassName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/FallbackLocationInformationTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/FallbackLocationInformationTest.kt
index fd831e9..52f12fa 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/FallbackLocationInformationTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/FallbackLocationInformationTest.kt
@@ -16,12 +16,12 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.getMethodByJvmName
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class FallbackLocationInformationTest {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/InternalModifierTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/InternalModifierTest.kt
index 7a380e5..66f907f 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/InternalModifierTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/InternalModifierTest.kt
@@ -16,12 +16,12 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.CompilationTestCapabilities
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.runKaptTest
 import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class InternalModifierTest {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt
index 623aa71..ea65ac3 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KotlinMetadataTest.kt
@@ -16,13 +16,13 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.testcode.KotlinTestClass
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.asJTypeName
 import androidx.room.compiler.processing.util.getMethodByJvmName
 import androidx.room.compiler.processing.util.getParameter
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class KotlinMetadataTest {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt
index 88e08ca..d486317 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/KspClassFileUtilityTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.ksp.KspTypeElement
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.XTestInvocation
@@ -24,6 +23,7 @@
 import androidx.room.compiler.processing.util.getAllFieldNames
 import androidx.room.compiler.processing.util.runKspTest
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import com.google.devtools.ksp.symbol.Origin
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MemoizedSequenceTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MemoizedSequenceTest.kt
index 704fd1d..2dc5ae9 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MemoizedSequenceTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MemoizedSequenceTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.MemoizedSequence
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class MemoizedSequenceTest {
@@ -74,7 +74,7 @@
         val result = kotlin.runCatching {
             memoized.iterator().next()
         }
-        assertThat(result.exceptionOrNull()).isInstanceOf<NoSuchElementException>()
+        assertThat(result.exceptionOrNull()).isInstanceOf(NoSuchElementException::class.java)
     }
 
     @Test
@@ -88,7 +88,7 @@
                 collected.add(iterator.next())
             }
         }
-        assertThat(result.exceptionOrNull()).isInstanceOf<NoSuchElementException>()
+        assertThat(result.exceptionOrNull()).isInstanceOf(NoSuchElementException::class.java)
         assertThat(collected).containsExactly(1, 2, 3)
     }
 }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
index eee3dd6..95fc55e 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.javac.JavacMethodElement
 import androidx.room.compiler.processing.javac.JavacTypeElement
 import androidx.room.compiler.processing.util.Source
@@ -27,6 +26,7 @@
 import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.testutils.generateAllEnumerations
 import com.google.auto.common.MoreTypes
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.MethodSpec
 import com.squareup.javapoet.ParameterSpec
 import java.io.File
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/OriginatingElementsTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/OriginatingElementsTest.kt
index 0d4d22f..177ca7e 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/OriginatingElementsTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/OriginatingElementsTest.kt
@@ -16,13 +16,13 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.ksp.KSClassDeclarationAsOriginatingElement
 import androidx.room.compiler.processing.ksp.KSFileAsOriginatingElement
 import androidx.room.compiler.processing.ksp.KspTypeElement
 import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticPropertyMethodElement
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.TypeSpec
 import javax.lang.model.element.ExecutableElement
 import javax.lang.model.element.TypeElement
@@ -57,7 +57,7 @@
             assertThat(originatingElement).isNotNull()
 
             if (it.isKsp) {
-                assertThat(originatingElement).isInstanceOf<KSFileAsOriginatingElement>()
+                assertThat(originatingElement).isInstanceOf(KSFileAsOriginatingElement::class.java)
 
                 val originatingFile = (originatingElement as KSFileAsOriginatingElement).ksFile
                 assertThat(originatingFile)
@@ -65,7 +65,7 @@
                         (element as KspTypeElement).declaration.containingFile
                     )
             } else {
-                assertThat(originatingElement).isInstanceOf<TypeElement>()
+                assertThat(originatingElement).isInstanceOf(TypeElement::class.java)
                 assertThat((originatingElement as TypeElement).qualifiedName.toString())
                     .isEqualTo("foo.bar.Baz")
             }
@@ -83,7 +83,7 @@
 
             if (it.isKsp) {
                 assertThat(originatingElement)
-                    .isInstanceOf<KSClassDeclarationAsOriginatingElement>()
+                    .isInstanceOf(KSClassDeclarationAsOriginatingElement::class.java)
 
                 val ksClassDeclaration =
                     (originatingElement as KSClassDeclarationAsOriginatingElement)
@@ -93,7 +93,7 @@
                         (element as KspTypeElement).declaration
                     )
             } else {
-                assertThat(originatingElement).isInstanceOf<TypeElement>()
+                assertThat(originatingElement).isInstanceOf(TypeElement::class.java)
                 assertThat((originatingElement as TypeElement).qualifiedName.toString())
                     .isEqualTo("com.google.devtools.ksp.processing.SymbolProcessor")
             }
@@ -129,7 +129,7 @@
 
                 if (invocation.isKsp) {
                     assertThat(originatingElement)
-                        .isInstanceOf<KSFileAsOriginatingElement>()
+                        .isInstanceOf(KSFileAsOriginatingElement::class.java)
 
                     val originatingFile = (originatingElement as KSFileAsOriginatingElement).ksFile
                     assertThat(originatingFile)
@@ -138,7 +138,7 @@
                                 .field.declaration.containingFile
                         )
                 } else {
-                    assertThat(originatingElement).isInstanceOf<ExecutableElement>()
+                    assertThat(originatingElement).isInstanceOf(ExecutableElement::class.java)
                 }
             }
         }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TopLevelMembersTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TopLevelMembersTest.kt
index 754142b..7e364c44 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TopLevelMembersTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TopLevelMembersTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.processing.ksp.KspExecutableElement
 import androidx.room.compiler.processing.ksp.KspFieldElement
 import androidx.room.compiler.processing.util.Source
@@ -24,6 +23,7 @@
 import androidx.room.compiler.processing.util.kspProcessingEnv
 import androidx.room.compiler.processing.util.kspResolver
 import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertWithMessage
 import com.google.devtools.ksp.KspExperimental
 import com.google.devtools.ksp.symbol.KSFunctionDeclaration
 import com.google.devtools.ksp.symbol.KSPropertyDeclaration
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAliasTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAliasTest.kt
index 9639527..517cbd6 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAliasTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAliasTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.asClassName
 import androidx.room.compiler.processing.util.CONTINUATION_JCLASS_NAME
@@ -27,6 +26,7 @@
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.getMethodByJvmName
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import com.squareup.kotlinpoet.LONG
 import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
 import com.squareup.kotlinpoet.javapoet.JParameterizedTypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAssignmentTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAssignmentTest.kt
index 1fc6e3a..38444c6 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAssignmentTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeAssignmentTest.kt
@@ -16,11 +16,11 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeInheritanceTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeInheritanceTest.kt
index 79977bf..4c27a38 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeInheritanceTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/TypeInheritanceTest.kt
@@ -16,13 +16,13 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.getMethodByJvmName
 import androidx.room.compiler.processing.util.getParameter
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
index 102650d..7a761a0 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.asClassName
 import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults
@@ -39,7 +37,8 @@
 import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
 import androidx.room.compiler.processing.util.typeName
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import com.squareup.javapoet.ClassName
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -172,8 +171,7 @@
                     }
                 annotation.getAsAnnotationBoxArray<OtherAnnotation>("otherAnnotationArray")
                     .let { boxArray ->
-                        // Kruth doesn't support arrays yet
-                        Truth.assertThat(boxArray).hasLength(2)
+                        assertThat(boxArray).hasLength(2)
                         assertThat(boxArray[0].value.value).isEqualTo("other list 1")
                         assertThat(boxArray[1].value.value).isEqualTo("other list 2")
                     }
@@ -272,8 +270,7 @@
                     }
                 annotation.getAsAnnotationBoxArray<OtherAnnotation>("otherAnnotationArray")
                     .let { boxArray ->
-                        // Kruth doesn't support arrays yet
-                        Truth.assertThat(boxArray).hasLength(2)
+                        assertThat(boxArray).hasLength(2)
                         assertThat(boxArray[0].value.value).isEqualTo("other list 1")
                         assertThat(boxArray[1].value.value).isEqualTo("other list 2")
                     }
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 b25e8f1..8698722 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,8 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.asClassName
 import androidx.room.compiler.processing.compat.XConverters.toJavac
@@ -44,6 +42,8 @@
 import androidx.room.compiler.processing.util.getParameter
 import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import com.squareup.kotlinpoet.javapoet.JAnnotationSpec
 import com.squareup.kotlinpoet.javapoet.JClassName
 import org.junit.Test
@@ -153,6 +153,40 @@
     }
 
     @Test
+    fun testJvmNameAnnotationValue() {
+        val kotlinSrc = Source.kotlin(
+            "MyAnnotation.kt",
+            """
+            @Target(AnnotationTarget.CLASS)
+            annotation class MyAnnotation(
+                @get:JvmName("stringParameter")
+                val stringParam: String,
+                val intParam: Int,
+                @get:JvmName("longParameter")
+                val longParam: Long
+            )
+            """.trimIndent()
+        )
+        val javaSrc = Source.java(
+            "Foo",
+            """
+            @MyAnnotation(stringParameter = "1", intParam = 2, longParameter = 3)
+            public class Foo {}
+            """.trimIndent()
+        )
+        runTest(sources = listOf(javaSrc, kotlinSrc)) { invocation ->
+            val typeElement = invocation.processingEnv.requireTypeElement("Foo")
+            val annotation =
+                typeElement.getAllAnnotations().single { it.qualifiedName == "MyAnnotation" }
+            assertThat(
+                annotation.annotationValues.map { it.value }
+            ).containsExactly(
+                "1", 2, 3.toLong()
+            ).inOrder()
+        }
+    }
+
+    @Test
     fun readsAnnotationsDeclaredInSources() {
         val source = Source.kotlin(
             "MyClass.kt",
@@ -1389,7 +1423,7 @@
                             .that(type.getAllAnnotationTypeElements())
                             .isEmpty()
                     } else {
-                        assertWithMessage("$desc type: $type")
+                        assertWithMessage("%s type: %s", desc, type.toString())
                             .that(type.getAllAnnotationTypeElements())
                             .containsExactly(a, b)
                         assertWithMessage("$desc type-argument: ${type.typeArguments[0]}")
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
index 866159c..f8b8666 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.codegen.JArrayTypeName
 import androidx.room.compiler.processing.compat.XConverters.toJavac
 import androidx.room.compiler.processing.util.Source
@@ -25,6 +24,7 @@
 import androidx.room.compiler.processing.util.asKClassName
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import com.squareup.kotlinpoet.ARRAY
 import com.squareup.kotlinpoet.BOOLEAN
 import com.squareup.kotlinpoet.BOOLEAN_ARRAY
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
index 11397a3..96a2bc9 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XArrayTypeTest.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.codegen.JArrayTypeName
 import androidx.room.compiler.processing.ksp.KspProcessingEnv
 import androidx.room.compiler.processing.ksp.createTypeReference
@@ -28,6 +26,8 @@
 import androidx.room.compiler.processing.util.kspResolver
 import androidx.room.compiler.processing.util.runKspTest
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
 import com.squareup.kotlinpoet.javapoet.JTypeName
 import com.squareup.kotlinpoet.javapoet.KTypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
index 1763a8b..1e47fd0 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.Subject
-import androidx.kruth.assertThat
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.asClassName
 import androidx.room.compiler.processing.javac.JavacTypeElement
@@ -37,6 +35,7 @@
 import androidx.room.compiler.processing.util.kspResolver
 import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
+import com.google.common.truth.Truth.assertThat
 import com.google.devtools.ksp.KspExperimental
 import com.google.devtools.ksp.symbol.KSFunctionDeclaration
 import com.google.devtools.ksp.symbol.KSPropertyDeclaration
@@ -929,12 +928,13 @@
             if (inv.isKsp) {
                 getTopLevelFunctionOrPropertyElements(inv, "foo.bar").forEach {
                         elem ->
-                    assertThat(elem.enclosingElement).isFileContainer(precompiled)
+                    assertThat(elem.enclosingElement).isInstanceOf(
+                        getFileContainerClass(precompiled))
                 }
             } else {
                 inv.processingEnv.getTypeElementsFromPackage("foo.bar").forEach {
                         typeElement ->
-                    assertThat(typeElement).isInstanceOf<JavacTypeElement>()
+                    assertThat(typeElement).isInstanceOf(JavacTypeElement::class.java)
                     assertThat(typeElement.enclosingElement).isNull()
 
                     typeElement.getEnclosedElements().forEach { elem ->
@@ -1008,10 +1008,12 @@
         ) { invocation, precompiled ->
             if (invocation.isKsp) {
                 getTopLevelFunctionOrPropertyElements(invocation, "foo.bar").forEach { elem ->
-                    assertThat(elem.closestMemberContainer).isFileContainer(precompiled)
+                    assertThat(elem.closestMemberContainer).isInstanceOf(
+                        getFileContainerClass(precompiled))
                     if (elem is XExecutableElement) {
                         elem.parameters.forEach { p ->
-                            assertThat(p.closestMemberContainer).isFileContainer(precompiled)
+                            assertThat(p.closestMemberContainer).isInstanceOf(
+                                getFileContainerClass(precompiled))
                         }
                     }
                 }
@@ -1129,13 +1131,12 @@
         }
         .filterNotNull()
 
-    private fun Subject<XElement>.isFileContainer(precompiled: Boolean) {
+    private fun getFileContainerClass(precompiled: Boolean) =
         if (precompiled) {
-            isInstanceOf<KspSyntheticFileMemberContainer>()
+            KspSyntheticFileMemberContainer::class.java
         } else {
-            isInstanceOf<KspFileMemberContainer>()
+            KspFileMemberContainer::class.java
         }
-    }
 
     private fun runProcessorTestHelper(
         sources: List<Source>,
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
index f30d2d6..df1e04b 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.asClassName
 import androidx.room.compiler.processing.util.CONTINUATION_JCLASS_NAME
@@ -30,6 +28,10 @@
 import androidx.room.compiler.processing.util.getParameter
 import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.typeName
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
@@ -46,9 +48,8 @@
 import java.io.IOException
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
+@RunWith(TestParameterInjector::class)
 class XExecutableElementTest {
     @Test
     fun basic() {
@@ -1347,4 +1348,85 @@
             }
         }
     }
+
+    @Test
+    fun parameterNames(
+        @TestParameter isJava: Boolean,
+        @TestParameter isPrecompiled: Boolean,
+        @TestParameter hasParametersFlag: Boolean,
+        @TestParameter hasDebugFlag: Boolean
+    ) {
+        val javaSource = Source.java(
+            "foo.bar.Baz",
+            """
+            package foo.bar;
+            public class Baz {
+                private Baz(String param1) {}
+            }
+            """.trimIndent())
+        val kotlinSource = Source.kotlin(
+            "foo.bar.Baz.kt",
+            """
+            package foo.bar
+            class Baz private constructor(param1: String)
+            """.trimIndent())
+
+        val sources: List<Source> =
+            if (isPrecompiled) {
+                emptyList()
+            } else {
+                if (isJava) {
+                    listOf(javaSource)
+                } else {
+                    listOf(kotlinSource)
+                }
+            }
+        val javacArgs = buildList {
+            if (hasParametersFlag) {
+                // This is used to generate `MethodParameters` in class files
+                add("-parameters")
+            }
+            if (hasDebugFlag) {
+                // This is used to generate `LocalVariableTable` in class files
+                add("-g:vars")
+            }
+        }
+        val classes: List<File> =
+            if (isPrecompiled) {
+                if (isJava) {
+                    compileFiles(listOf(javaSource), javacArguments = javacArgs)
+                } else {
+                    compileFiles(listOf(kotlinSource), javacArguments = javacArgs)
+                }
+            } else {
+                emptyList()
+            }
+        runProcessorTest(sources = sources, classpath = classes) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            assertThat(element.getConstructors().single().parameters.single().name)
+                .isEqualTo(
+                    if (isJava) {
+                        if (isPrecompiled) {
+                            if (hasParametersFlag) {
+                                "param1"
+                            } else {
+                                if (it.isKsp) {
+                                    "p0"
+                                } else { // Javac/KAPT
+                                    if (hasDebugFlag) {
+                                        "param1"
+                                    } else {
+                                        "arg0"
+                                    }
+                                }
+                            }
+                        } else { // Java sources
+                            "param1"
+                        }
+                    } else { // Kotlin sources or classes
+                        "param1"
+                    }
+                )
+        }
+    }
 }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
index 95adf50..4c88042 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableTypeTest.kt
@@ -16,14 +16,13 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.CONTINUATION_JCLASS_NAME
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.UNIT_JCLASS_NAME
 import androidx.room.compiler.processing.util.getMethodByJvmName
 import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.typeName
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
@@ -100,7 +99,7 @@
                 vararg subjects: XTypeElement,
                 callback: (XMethodType) -> Unit
             ) {
-                Truth.assertThat(subjects).isNotEmpty() // Kruth doesn't support arrays yet
+                assertThat(subjects).isNotEmpty()
                 subjects.forEach {
                     callback(myInterface.getMethodByJvmName(methodName).asMemberOf(it.type))
                     callback(it.getMethodByJvmName(methodName).asMemberOf(it.type))
@@ -441,7 +440,7 @@
                 vararg subjects: XTypeElement,
                 callback: (XMethodType) -> Unit
             ) {
-                Truth.assertThat(subjects).isNotEmpty() // Kruth doesn't support arrays yet
+                assertThat(subjects).isNotEmpty()
                 subjects.forEach {
                     callback(myInterface.getMethodByJvmName(methodName).asMemberOf(it.type))
                     callback(it.getMethodByJvmName(methodName).asMemberOf(it.type))
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XFilerTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XFilerTest.kt
index 8029095..209d88e 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XFilerTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XFilerTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import kotlin.io.path.Path
 import org.junit.Assert.fail
 import org.junit.Test
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt
index b2556ad..7574266 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XMessagerTest.kt
@@ -16,9 +16,9 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import javax.tools.Diagnostic
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
index 165deab..313e735c 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XNullabilityTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.XNullability.NONNULL
 import androidx.room.compiler.processing.XNullability.NULLABLE
 import androidx.room.compiler.processing.XNullability.UNKNOWN
@@ -26,6 +25,7 @@
 import androidx.room.compiler.processing.util.getParameter
 import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.runProcessorTestWithoutKsp
+import com.google.common.truth.Truth.assertThat
 import com.squareup.kotlinpoet.INT
 import com.squareup.kotlinpoet.UNIT
 import com.squareup.kotlinpoet.javapoet.JTypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvConfigTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvConfigTest.kt
index 65972b9..037b239 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvConfigTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvConfigTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class XProcessingEnvConfigTest {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
index d2452bd..33ff8fc 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingEnvTest.kt
@@ -16,12 +16,12 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.codegen.XClassName
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.asClassName
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.JavaFile
 import com.squareup.javapoet.TypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingStepTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingStepTest.kt
index d956499..5e4141e 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingStepTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XProcessingStepTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.javac.JavacBasicAnnotationProcessor
 import androidx.room.compiler.processing.ksp.KspBasicAnnotationProcessor
 import androidx.room.compiler.processing.ksp.KspElement
@@ -31,6 +30,7 @@
 import androidx.room.compiler.processing.util.compiler.compile
 import androidx.room.compiler.processing.util.runProcessorTest
 import com.google.common.truth.Truth.assertAbout
+import com.google.common.truth.Truth.assertThat
 import com.google.devtools.ksp.processing.SymbolProcessor
 import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
 import com.google.devtools.ksp.processing.SymbolProcessorProvider
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRawTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRawTypeTest.kt
index f507ee8..6d6ca5b 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRawTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRawTypeTest.kt
@@ -16,9 +16,9 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeVariableName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRoundEnvTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRoundEnvTest.kt
index ac2658a..eb733a0 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRoundEnvTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XRoundEnvTest.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.codegen.XClassName
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.processing.testcode.OtherAnnotation
@@ -25,6 +23,8 @@
 import androidx.room.compiler.processing.util.getDeclaredMethodByJvmName
 import androidx.room.compiler.processing.util.runKspTest
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import com.squareup.kotlinpoet.INT
 import com.squareup.kotlinpoet.UNIT
 import com.squareup.kotlinpoet.javapoet.JTypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
index b69db6e..e6d31ce 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.codegen.XClassName
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.asClassName
@@ -32,6 +30,8 @@
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.getMethodByJvmName
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import com.squareup.kotlinpoet.INT
 import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
 import com.squareup.kotlinpoet.javapoet.JClassName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt
index d14fea3..48a219f 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeParameterElementTest.kt
@@ -16,9 +16,9 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
index 1a68ad3..719fb84 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.compiler.processing
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.codegen.XClassName
 import androidx.room.compiler.codegen.XTypeName.Companion.ANY_OBJECT
 import androidx.room.compiler.codegen.XTypeName.Companion.UNAVAILABLE_KTYPE_NAME
@@ -38,6 +36,8 @@
 import androidx.room.compiler.processing.util.kspResolver
 import androidx.room.compiler.processing.util.runKspTest
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import com.google.devtools.ksp.getClassDeclarationByName
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt
index d229610..a287249 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing.compat
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.compat.XConverters.getProcessingEnv
 import androidx.room.compiler.processing.compat.XConverters.toJavac
@@ -34,6 +33,7 @@
 import androidx.room.compiler.processing.util.runProcessorTest
 import com.google.auto.common.MoreElements
 import com.google.auto.common.MoreTypes
+import com.google.common.truth.Truth.assertThat
 import com.google.devtools.ksp.getDeclaredFunctions
 import com.google.devtools.ksp.processing.impl.KSNameImpl
 import com.squareup.javapoet.ClassName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
index 86d3822..15156c4 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
@@ -16,9 +16,9 @@
 
 package androidx.room.compiler.processing.javac.kotlin
 
-import androidx.kruth.assertThat
 import com.google.auto.common.MoreElements
 import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import com.google.testing.compile.JavaFileObjects
 import com.google.testing.compile.JavaSourcesSubjectFactory
 import com.squareup.javapoet.ArrayTypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
index eb676b6..3db463b 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.compiler.processing.javac.kotlin
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.XProcessingEnvConfig
 import androidx.room.compiler.processing.javac.JavacProcessingEnv
@@ -27,6 +25,8 @@
 import androidx.room.compiler.processing.util.runJavaProcessorTest
 import androidx.room.compiler.processing.util.runKaptTest
 import androidx.room.compiler.processing.util.sanitizeAsJavaParameterName
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import javax.annotation.processing.ProcessingEnvironment
 import javax.lang.model.element.ExecutableElement
 import javax.lang.model.element.TypeElement
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt
index a6e1551..8f9a50a 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSAsMemberOfTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing.ksp
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.className
@@ -24,6 +23,7 @@
 import androidx.room.compiler.processing.util.getMethodByJvmName
 import androidx.room.compiler.processing.util.runKspTest
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
index 05c5504..61eee03 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
@@ -16,13 +16,13 @@
 
 package androidx.room.compiler.processing.ksp
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.className
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.kspResolver
 import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import com.google.devtools.ksp.getDeclaredProperties
 import com.google.devtools.ksp.symbol.KSClassDeclaration
 import com.squareup.javapoet.ClassName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverWithTypeParametersTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverWithTypeParametersTest.kt
index 3053d5e..595d75c 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverWithTypeParametersTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolverWithTypeParametersTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing.ksp
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.XMethodType
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.util.Source
@@ -24,6 +23,7 @@
 import androidx.room.compiler.processing.util.getDeclaredMethodByJvmName
 import androidx.room.compiler.processing.util.runKaptTest
 import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
index 7e2b275..688c746 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFieldElementTest.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.compiler.processing.ksp
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.processing.XFieldElement
 import androidx.room.compiler.processing.ksp.KspFieldElementTest.TestModifier.FINAL
 import androidx.room.compiler.processing.ksp.KspFieldElementTest.TestModifier.PRIVATE
@@ -30,6 +28,8 @@
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.runProcessorTest
 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.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
 import com.squareup.javapoet.TypeVariableName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt
index 73af6bc..62c1489 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing.ksp
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.XAnnotation
 import androidx.room.compiler.processing.XAnnotationValue
 import androidx.room.compiler.processing.XElement
@@ -24,6 +23,7 @@
 import androidx.room.compiler.processing.addOriginatingElement
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
 import com.google.devtools.ksp.processing.CodeGenerator
 import com.google.devtools.ksp.processing.Dependencies
 import com.google.devtools.ksp.symbol.KSClassDeclaration
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspJvmDescriptorUtilsTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspJvmDescriptorUtilsTest.kt
index a0781bf..2e74202 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspJvmDescriptorUtilsTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspJvmDescriptorUtilsTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing.ksp
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.XElement
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.compiler.processing.isConstructor
@@ -27,6 +26,7 @@
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspProcessingEnvTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspProcessingEnvTest.kt
index eefdb4b..417c63b 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspProcessingEnvTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspProcessingEnvTest.kt
@@ -16,10 +16,10 @@
 
 package androidx.room.compiler.processing.ksp
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.util.kspResolver
 import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.TypeName
 import org.junit.Test
 
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBoxTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBoxTest.kt
index c4e1e87..be5dea0 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBoxTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspReflectiveAnnotationBoxTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.compiler.processing.ksp
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.TypeName
 import kotlin.reflect.KClass
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
index a97fc52..68f7b5f5 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.compiler.processing.ksp
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.processing.XExecutableElement
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.util.CompilationTestCapabilities
@@ -26,6 +24,8 @@
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.runKaptTest
 import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import com.squareup.javapoet.TypeName
 import org.junit.Test
 
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
index 1808bb1..9c69e84 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing.ksp
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.codegen.XClassName
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.asClassName
@@ -35,6 +34,7 @@
 import androidx.room.compiler.processing.util.getMethodByJvmName
 import androidx.room.compiler.processing.util.runKspTest
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import com.squareup.kotlinpoet.TypeVariableName
 import com.squareup.kotlinpoet.UNIT
 import com.squareup.kotlinpoet.javapoet.JClassName
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticFileMemberContainerTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticFileMemberContainerTest.kt
index 134aed5..86ea00f 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticFileMemberContainerTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticFileMemberContainerTest.kt
@@ -16,14 +16,14 @@
 
 package androidx.room.compiler.processing.ksp.synthetic
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.processing.ksp.KspFieldElement
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.kspResolver
 import androidx.room.compiler.processing.util.runKspTest
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import com.google.devtools.ksp.KspExperimental
 import com.google.devtools.ksp.symbol.KSPropertyDeclaration
 import org.junit.Test
diff --git a/room/room-compiler/build.gradle b/room/room-compiler/build.gradle
index 6d06fe8..6c70d6b 100644
--- a/room/room-compiler/build.gradle
+++ b/room/room-compiler/build.gradle
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 import androidx.build.BuildOnServerKt
 import androidx.build.LibraryType
 import androidx.build.SdkHelperKt
@@ -29,12 +28,6 @@
     id("com.github.johnrengelman.shadow")
 }
 
-def antlrOut = "$buildDir/generated/antlr/grammar-gen/"
-sourceSets {
-    main.java.srcDirs += "src/main/grammar-gen"
-    main.java.srcDirs += antlrOut
-}
-
 configurations {
     /**
      * shadowed is used for dependencies which we jarjar into the library jar instead of adding it
@@ -103,7 +96,6 @@
     implementation(libs.apacheCommonsCodec)
     implementation(libs.intellijAnnotations)
     testImplementation(libs.truth)
-    testImplementation(project(":kruth:kruth"))
     testImplementation(libs.testParameterInjector)
     testImplementation(libs.autoValue) // to access the processor in tests
     testImplementation(libs.autoServiceAnnotations)
@@ -121,23 +113,27 @@
     testImplementation(project(":internal-testutils-common"))
 }
 
-def generateAntlrTask = task("generateAntlrGrammar", type: GenerateAntlrGrammar) {
-    sqliteFile = file("$projectDir/SQLite.g4")
-    antlrClasspath = configurations.compileClasspath
-    outputDirectory = file(antlrOut)
+def generateAntlrTask = tasks.register("generateAntlrGrammar", GenerateAntlrGrammar) { task ->
+    task.getSqliteFile().set(layout.projectDirectory.file("SQLite.g4"))
+    task.getAntlrClasspath().from(configurations.compileClasspath)
+    task.getOutputDirectory().set(layout.buildDirectory.dir("generated/antlr/grammar-gen/"))
+}
+
+sourceSets {
+    main.java.srcDirs += generateAntlrTask.map { it.outputDirectory }
 }
 
 @CacheableTask
 abstract class GenerateAntlrGrammar extends DefaultTask {
     @PathSensitive(PathSensitivity.NONE)
     @InputFile
-    File sqliteFile
+    abstract RegularFileProperty getSqliteFile()
 
     @Classpath
-    FileCollection antlrClasspath
+    abstract ConfigurableFileCollection getAntlrClasspath()
 
     @OutputDirectory
-    File outputDirectory
+    abstract DirectoryProperty getOutputDirectory()
 
     @Inject
     abstract ExecOperations getExecOperations()
@@ -152,10 +148,10 @@
     void generateAntlrGrammar() {
         execOperations.javaexec {
             mainClass.set("org.antlr.v4.Tool")
-            classpath = antlrClasspath
-            args "SQLite.g4",
+            classpath = getAntlrClasspath()
+            args getSqliteFile().asFile.get().absolutePath,
                  "-visitor",
-                 "-o", new File(outputDirectory, "androidx/room/parser").path,
+                 "-o", new File(getOutputDirectory().asFile.get(), "androidx/room/parser").path,
                  "-package", "androidx.room.parser"
         }
     }
@@ -165,23 +161,23 @@
  * Room compiler jarjars some dependencies. This task validates the published artifacts of room
  * compiler to ensure dependencies are properly jarjarred.
  */
-class CheckArtifactTask extends DefaultTask {
+abstract class CheckArtifactTask extends DefaultTask {
     @InputFiles
-    FileCollection artifactInputs = project.objects.fileCollection()
+    abstract ConfigurableFileCollection getArtifactInputs()
     @InputFile
-    File pomFile
+    abstract RegularFileProperty getPomFile()
     @OutputFile
-    File result = new File(project.buildDir, "checkArtifactOutput.txt")
+    abstract RegularFileProperty getResult()
     /**
      * Checks the publish task's artifacts to make sure the classes.jar does include jarjarred
      * antlr classes.
      */
-    def validatePublishTaskOutputs() {
-        if (artifactInputs.files.isEmpty()) {
+    void validatePublishTaskOutputs() {
+        if (getArtifactInputs().files.isEmpty()) {
             throw new GradleException("Couldn't find the classes.jar for the room-compiler " +
                     "artifact. Ensure that publish is setup properly.")
         }
-        artifactInputs.forEach {
+        getArtifactInputs().forEach {
             validateJarContents(it)
         }
     }
@@ -190,7 +186,7 @@
      * Traverses the given jar file, looks for the classes that should be jarjarred and validates
      * their location.
      */
-    def validateJarContents(File jarFile) {
+    static void validateJarContents(File jarFile) {
         Boolean found = false
         ZipFile zip = new ZipFile(jarFile)
         try {
@@ -223,11 +219,12 @@
      * Checks the generated pom file to ensure it does not depend on any jarjarred dependencies
      * but still depends on others.
      */
-    def validatePomTaskOutputs() {
-        if (!pomFile.canRead()) {
+    void validatePomTaskOutputs() {
+        File pom = getPomFile().asFile.get()
+        if (!pom.canRead()) {
             throw new GradleException("Cannot find the pom file for room-compiler")
         }
-        def pomContents = pomFile.newReader().text
+        def pomContents = pom.newReader().text
         if (pomContents.contains("antlr")) {
             throw new GradleException("Room-compiler pom file should not depend on antlr.\n" +
                     "Pom Contents:\n $pomContents")
@@ -239,24 +236,29 @@
     }
 
     @TaskAction
-    def validate() {
-        result.write("fail\n")
+    void validate() {
+        getResult().asFile.get().write("fail\n")
         validatePublishTaskOutputs()
         validatePomTaskOutputs()
         // have a no-op output to make gradle happy w/ input/output checking.
-        result.write("ok\n")
+        getResult().asFile.get().write("ok\n")
     }
 }
 
-def checkArtifactContentsTask = tasks.register("checkArtifactTask", CheckArtifactTask) {
-    def pomTask = (GenerateMavenPom) project.tasks.named("generatePomFileForMavenPublication").get()
-    it.pomFile = pomTask.destination
+def checkArtifactContentsTask = tasks.register("checkArtifact", CheckArtifactTask) { task ->
+    task.getResult().set(layout.buildDirectory.file("checkArtifactOutput.txt"))
+    def pomTask = (TaskProvider<GenerateMavenPom>) project.tasks.named("generatePomFileForMavenPublication")
+    task.getPomFile().set(
+            project.objects.fileProperty().fileProvider(
+                    pomTask.map {  it.destination }
+            )
+    )
 }
 
 afterEvaluate {
     def publishTaskProvider = project.tasks.named("publishMavenPublicationToMavenRepository")
-    checkArtifactContentsTask.configure {
-        it.artifactInputs.from {
+    checkArtifactContentsTask.configure { checkArtifactTask ->
+        checkArtifactTask.getArtifactInputs().from {
             publishTaskProvider.map {
                 ((PublishToMavenRepository) it).getPublication().artifacts.matching {
                     it.classifier == null
@@ -271,11 +273,6 @@
 // make sure we validate published artifacts on the build server.
 BuildOnServerKt.addToBuildOnServer(project, checkArtifactContentsTask)
 
-def tasksThatDependOnAntlr = ["compileKotlin", "sourceJar", "kotlinSourcesJar", "generateKmpDocs"]
-tasks.matching { tasksThatDependOnAntlr.contains(it.name) }.configureEach {
-    it.dependsOn(generateAntlrTask)
-}
-
 tasks.withType(KotlinCompile).configureEach {
     kotlinOptions {
         freeCompilerArgs += [
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/ext/ElementExtTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/ext/ElementExtTest.kt
index 1c19e7f..5f216dc 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/ext/ElementExtTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/ext/ElementExtTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.ext
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.codegen.XClassName
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.processing.XMethodElement
@@ -24,6 +23,7 @@
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/parser/SQLTypeAffinityTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/parser/SQLTypeAffinityTest.kt
index 0db9225..346c9fe 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/parser/SQLTypeAffinityTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/parser/SQLTypeAffinityTest.kt
@@ -16,11 +16,11 @@
 
 package androidx.room.parser
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class SQLTypeAffinityTest {
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/parser/SqlParserTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/parser/SqlParserTest.kt
index b3e20de..9d07223 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/parser/SqlParserTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/parser/SqlParserTest.kt
@@ -15,7 +15,7 @@
  */
 package androidx.room.parser
 
-import androidx.kruth.assertThat
+import com.google.common.truth.Truth
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.not
 import org.hamcrest.MatcherAssert.assertThat
@@ -277,31 +277,31 @@
     @Test
     fun hasTopStarProjection() {
         SqlParser.parse("SELECT * FROM Foo").let {
-            assertThat(it.hasTopStarProjection).isTrue()
+            Truth.assertThat(it.hasTopStarProjection).isTrue()
         }
         SqlParser.parse("SELECT Foo.* FROM Foo").let {
-            assertThat(it.hasTopStarProjection).isTrue()
+            Truth.assertThat(it.hasTopStarProjection).isTrue()
         }
         SqlParser.parse("SELECT 0 as new, Foo.* FROM Foo").let {
-            assertThat(it.hasTopStarProjection).isTrue()
+            Truth.assertThat(it.hasTopStarProjection).isTrue()
         }
         SqlParser.parse("SELECT f.* FROM Foo f").let {
-            assertThat(it.hasTopStarProjection).isTrue()
+            Truth.assertThat(it.hasTopStarProjection).isTrue()
         }
         SqlParser.parse("SELECT id FROM Foo").let {
-            assertThat(it.hasTopStarProjection).isFalse()
+            Truth.assertThat(it.hasTopStarProjection).isFalse()
         }
         SqlParser.parse("SELECT id FROM (SELECT * FROM Foo)").let {
-            assertThat(it.hasTopStarProjection).isFalse()
+            Truth.assertThat(it.hasTopStarProjection).isFalse()
         }
         SqlParser.parse("SELECT COUNT(*) FROM Foo").let {
-            assertThat(it.hasTopStarProjection).isFalse()
+            Truth.assertThat(it.hasTopStarProjection).isFalse()
         }
         SqlParser.parse("SELECT (SELECT * FROM Foo LIMIT 1) == 1 as uno FROM Foo").let {
-            assertThat(it.hasTopStarProjection).isFalse()
+            Truth.assertThat(it.hasTopStarProjection).isFalse()
         }
         SqlParser.parse("INSERT INTO Foo VALUES (:param)").let {
-            assertThat(it.hasTopStarProjection).isNull()
+            Truth.assertThat(it.hasTopStarProjection).isNull()
         }
     }
 
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/CustomConverterProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/CustomConverterProcessorTest.kt
index b3a46be..a4da24b 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/CustomConverterProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/CustomConverterProcessorTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.processor
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.codegen.VisibilityModifier
 import androidx.room.compiler.codegen.XAnnotationSpec
@@ -39,6 +38,7 @@
 import androidx.room.processor.ProcessorErrors.TYPE_CONVERTER_UNBOUND_GENERIC
 import androidx.room.testing.context
 import androidx.room.vo.CustomTypeConverter
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.TypeVariableName
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt
index dd5133d..076ccf0 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt
@@ -17,7 +17,6 @@
 package androidx.room.processor
 
 import COMMON
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.isTypeElement
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.XTestInvocation
@@ -31,6 +30,7 @@
 import androidx.room.vo.Dao
 import androidx.room.vo.ReadQueryMethod
 import androidx.room.vo.Warning
+import com.google.common.truth.Truth
 import createVerifierFromEntitiesAndViews
 import java.io.File
 import org.hamcrest.CoreMatchers.`is`
@@ -277,7 +277,7 @@
             val dbType = invocation.context.processingEnv.requireType(ROOM_DB)
             val daoProcessor =
                 DaoProcessor(invocation.context, dao, dbType, null)
-            assertThat(daoProcessor.context.logger.suppressedWarnings)
+            Truth.assertThat(daoProcessor.context.logger.suppressedWarnings)
                 .containsExactly(Warning.CURSOR_MISMATCH)
         }
     }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseProcessorTest.kt
index c8ce1d7..a4c6126 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseProcessorTest.kt
@@ -17,7 +17,6 @@
 package androidx.room.processor
 
 import COMMON
-import androidx.kruth.assertThat
 import androidx.room.DatabaseProcessingStep
 import androidx.room.RoomProcessor
 import androidx.room.compiler.codegen.CodeLanguage
@@ -42,6 +41,7 @@
 import androidx.room.vo.ReadQueryMethod
 import androidx.room.vo.Warning
 import com.google.auto.service.processor.AutoServiceProcessor
+import com.google.common.truth.Truth.assertThat
 import java.io.File
 import java.io.FileOutputStream
 import java.net.URL
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseViewProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseViewProcessorTest.kt
index a3f9328..93870ff 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseViewProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/DatabaseViewProcessorTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.processor
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.runProcessorTest
@@ -25,6 +24,7 @@
 import androidx.room.testing.context
 import androidx.room.verifier.ColumnInfo
 import androidx.room.vo.DatabaseView
+import com.google.common.truth.Truth.assertThat
 import createVerifierFromEntitiesAndViews
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/EntityNameMatchingVariationsTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/EntityNameMatchingVariationsTest.kt
index 6781bb3..c71cc23 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/EntityNameMatchingVariationsTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/EntityNameMatchingVariationsTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.processor
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.parser.SQLTypeAffinity
@@ -24,6 +23,7 @@
 import androidx.room.vo.Field
 import androidx.room.vo.FieldGetter
 import androidx.room.vo.FieldSetter
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt
index 45f42288..d00d449 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt
@@ -17,7 +17,6 @@
 package androidx.room.processor
 
 import COMMON
-import androidx.kruth.assertThat
 import androidx.room.Dao
 import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.codegen.XClassName
@@ -39,6 +38,7 @@
 import androidx.room.solver.shortcut.result.InsertOrUpsertMethodAdapter
 import androidx.room.testing.context
 import androidx.room.vo.InsertOrUpsertShortcutMethod
+import com.google.common.truth.Truth.assertThat
 import kotlin.reflect.KClass
 import org.junit.Test
 
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertionMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertionMethodProcessorTest.kt
index 3550ce5..55f7026 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertionMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertionMethodProcessorTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.processor
 
-import androidx.kruth.assertThat
 import androidx.room.Insert
 import androidx.room.OnConflictStrategy
 import androidx.room.compiler.processing.XMethodElement
@@ -26,6 +25,7 @@
 import androidx.room.processor.ProcessorErrors.INSERT_MULTI_PARAM_SINGLE_RETURN_MISMATCH
 import androidx.room.processor.ProcessorErrors.INSERT_SINGLE_PARAM_MULTI_RETURN_MISMATCH
 import androidx.room.vo.InsertionMethod
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
index a4c0fd9..723aea9 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
@@ -17,7 +17,6 @@
 package androidx.room.processor
 
 import COMMON
-import androidx.kruth.assertThat
 import androidx.room.Embedded
 import androidx.room.compiler.codegen.XClassName
 import androidx.room.compiler.processing.XFieldElement
@@ -42,6 +41,7 @@
 import androidx.room.vo.FieldSetter
 import androidx.room.vo.Pojo
 import androidx.room.vo.RelationCollector
+import com.google.common.truth.Truth
 import java.io.File
 import org.hamcrest.CoreMatchers.instanceOf
 import org.hamcrest.CoreMatchers.`is`
@@ -2092,7 +2092,7 @@
                 it.name
             }
             val stringType = invocation.context.COMMON_TYPES.STRING
-            assertThat(
+            Truth.assertThat(
                 fields["isbn"]?.getter
             ).isEqualTo(
                 FieldGetter(
@@ -2102,7 +2102,7 @@
                     callType = CallType.SYNTHETIC_METHOD
                 )
             )
-            assertThat(
+            Truth.assertThat(
                 fields["isbn"]?.setter
             ).isEqualTo(
                 FieldSetter(
@@ -2113,7 +2113,7 @@
                 )
             )
 
-            assertThat(
+            Truth.assertThat(
                 fields["isbn2"]?.getter
             ).isEqualTo(
                 FieldGetter(
@@ -2123,7 +2123,7 @@
                     callType = CallType.SYNTHETIC_METHOD
                 )
             )
-            assertThat(
+            Truth.assertThat(
                 fields["isbn2"]?.setter
             ).isEqualTo(
                 FieldSetter(
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
index 13d0293..2fde4d7 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
@@ -17,7 +17,6 @@
 package androidx.room.processor
 
 import COMMON
-import androidx.kruth.assertThat
 import androidx.room.Dao
 import androidx.room.Query
 import androidx.room.compiler.codegen.CodeLanguage
@@ -58,6 +57,7 @@
 import androidx.room.vo.ReadQueryMethod
 import androidx.room.vo.Warning
 import androidx.room.vo.WriteQueryMethod
+import com.google.common.truth.Truth.assertThat
 import createVerifierFromEntitiesAndViews
 import mockElementAndType
 import org.hamcrest.CoreMatchers.hasItem
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
index a5a8112..e7d8207 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/TableEntityProcessorTest.kt
@@ -17,7 +17,6 @@
 package androidx.room.processor
 
 import COMMON
-import androidx.kruth.assertThat
 import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.XTypeName.Companion.PRIMITIVE_LONG
@@ -35,6 +34,7 @@
 import androidx.room.vo.Index
 import androidx.room.vo.Pojo
 import androidx.room.vo.columnNames
+import com.google.common.truth.Truth.assertThat
 import org.hamcrest.CoreMatchers.hasItems
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.MatcherAssert.assertThat
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt
index 997f6d3..cfd3ada 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.solver
 
-import androidx.kruth.assertThat
 import androidx.room.RoomKspProcessor
 import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.processing.XType
@@ -34,6 +33,7 @@
 import androidx.room.testing.context
 import androidx.room.vo.BuiltInConverterFlags
 import androidx.room.writer.DaoWriter
+import com.google.common.truth.Truth.assertThat
 import javax.tools.Diagnostic
 import org.junit.Rule
 import org.junit.Test
@@ -800,7 +800,7 @@
             )
         ) { invocation ->
             val store = invocation.createStore(*selectedConverters)
-            assertThat(store).isInstanceOf<NullAwareTypeConverterStore>()
+            assertThat(store).isInstanceOf(NullAwareTypeConverterStore::class.java)
             val myClassTypeElement = invocation.processingEnv.requireTypeElement(
                 "MyClass"
             )
@@ -860,7 +860,7 @@
             )
         ) { invocation ->
             val store = invocation.createStore(*selectedConverters)
-            assertThat(store).isInstanceOf<NullAwareTypeConverterStore>()
+            assertThat(store).isInstanceOf(NullAwareTypeConverterStore::class.java)
             val myClassTypeElement = invocation.processingEnv.requireTypeElement(
                 "MyClass"
             )
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
index 53c6258..f5c3ac3 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
@@ -17,7 +17,6 @@
 package androidx.room.solver
 
 import COMMON
-import androidx.kruth.assertThat
 import androidx.paging.DataSource
 import androidx.paging.PagingSource
 import androidx.room.Dao
@@ -77,6 +76,7 @@
 import androidx.room.testing.context
 import androidx.room.vo.BuiltInConverterFlags
 import androidx.room.vo.ReadQueryMethod
+import com.google.common.truth.Truth.assertThat
 import org.hamcrest.CoreMatchers.instanceOf
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.notNullValue
@@ -351,7 +351,7 @@
             )
 
             assertThat(adapter).isNotNull()
-            assertThat(adapter).isInstanceOf<UuidColumnTypeAdapter>()
+            assertThat(adapter).isInstanceOf(UuidColumnTypeAdapter::class.java)
         }
     }
 
@@ -1556,7 +1556,7 @@
                     isMultipleParameter = true
                 )
                 assertThat(adapter).isNotNull()
-                assertThat(adapter).isInstanceOf<CollectionQueryParameterAdapter>()
+                assertThat(adapter).isInstanceOf(CollectionQueryParameterAdapter::class.java)
             }
         }
     }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterCostTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterCostTest.kt
index 7029a46..0443054 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterCostTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterCostTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.solver
 
-import androidx.kruth.assertThat
 import androidx.room.solver.types.TypeConverter.Cost
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class TypeConverterCostTest {
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterStoreTest.kt
index d2fc717..df7480e 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeConverterStoreTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.solver
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.runProcessorTest
@@ -26,6 +25,7 @@
 import androidx.room.solver.types.TypeConverter
 import androidx.room.testing.context
 import androidx.room.vo.BuiltInConverterFlags
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/query/QueryWriterTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/query/QueryWriterTest.kt
index 650b393..e334883 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/query/QueryWriterTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/query/QueryWriterTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.solver.query
 
-import androidx.kruth.assertThat
 import androidx.room.Dao
 import androidx.room.Query
 import androidx.room.compiler.codegen.CodeLanguage
@@ -28,6 +27,7 @@
 import androidx.room.processor.QueryMethodProcessor
 import androidx.room.testing.context
 import androidx.room.writer.QueryWriter
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt
index b9251a5..e886664 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/testing/InProcessorTest.kt
@@ -16,10 +16,10 @@
 
 package androidx.room.testing
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.CompilationTestCapabilities
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/util/SchemaDifferTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/util/SchemaDifferTest.kt
index 5c766ef..bec007f 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/util/SchemaDifferTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/util/SchemaDifferTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.util
 
-import androidx.kruth.assertThat
 import androidx.room.migration.bundle.DatabaseBundle
 import androidx.room.migration.bundle.EntityBundle
 import androidx.room.migration.bundle.FieldBundle
@@ -27,6 +26,7 @@
 import androidx.room.migration.bundle.TABLE_NAME_PLACEHOLDER
 import androidx.room.processor.ProcessorErrors
 import androidx.room.vo.AutoMigration
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.fail
 import org.junit.Test
 
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/util/SimpleJavaVersionTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/util/SimpleJavaVersionTest.kt
index 7c76004..4a2e2f7 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/util/SimpleJavaVersionTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/util/SimpleJavaVersionTest.kt
@@ -16,7 +16,7 @@
 
 package androidx.room.util
 
-import androidx.kruth.assertThat
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.fail
 import org.junit.Test
 
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt
index 3ee50134..6d6fcc7 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt
@@ -17,7 +17,6 @@
 package androidx.room.writer
 
 import COMMON
-import androidx.kruth.StringSubject
 import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.compiler.processing.util.Source
@@ -25,6 +24,7 @@
 import androidx.room.ext.RoomTypeNames.ROOM_DB
 import androidx.room.processor.DaoProcessor
 import androidx.room.testing.context
+import com.google.common.truth.StringSubject
 import createVerifierFromEntitiesAndViews
 import org.jetbrains.kotlin.config.JvmDefaultMode
 import org.junit.Test
diff --git a/settings.gradle b/settings.gradle
index 6d5d849..ae52660 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -973,6 +973,7 @@
 includeProject(":slice:slice-test", [BuildType.MAIN])
 includeProject(":slice:slice-view", [BuildType.MAIN])
 includeProject(":slidingpanelayout:slidingpanelayout", [BuildType.MAIN, BuildType.FLAN])
+includeProject(":slidingpanelayout:slidingpanelayout-testapp", [BuildType.MAIN])
 includeProject(":sqlite:integration-tests:inspection-room-testapp", [BuildType.MAIN])
 includeProject(":sqlite:integration-tests:inspection-sqldelight-testapp", [BuildType.MAIN])
 includeProject(":sqlite:sqlite", [BuildType.MAIN, BuildType.COMPOSE])
diff --git a/slidingpanelayout/slidingpanelayout-testapp/build.gradle b/slidingpanelayout/slidingpanelayout-testapp/build.gradle
new file mode 100644
index 0000000..7d5ebec
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 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
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.application")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    api(libs.kotlinStdlib)
+    // Add dependencies here
+    implementation(project(":slidingpanelayout:slidingpanelayout"))
+    implementation("androidx.recyclerview:recyclerview:1.2.1")
+}
+
+android {
+    namespace "androidx.slidingpanelayout.demo"
+}
+
+androidx {
+    name = "androidx.slidingpanelayout:slidingpanelayout-demo"
+    type = LibraryType.SAMPLES
+    inceptionYear = "2023"
+    description = "SlidingPaneLayout Demos"
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/AndroidManifest.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e026b28
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+    <application
+        android:allowBackup="true"
+        android:label="SlidingPaneLayout Demo"
+        android:supportsRtl="true">
+
+        <activity android:name="androidx.slidingpanelayout.SlidingPaneLayoutDemos"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name="androidx.slidingpanelayout.SlidingPaneLayoutSample"
+            android:exported="true" />
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/DemoItem.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/DemoItem.kt
new file mode 100644
index 0000000..5415371
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/DemoItem.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.slidingpanelayout
+
+import android.app.Activity
+
+class DemoItem<T : Activity>(
+    val clazz: Class<T>,
+    val title: String,
+    val description: String
+)
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemAdapter.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemAdapter.kt
new file mode 100644
index 0000000..93fac85
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemAdapter.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 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.slidingpanelayout
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import androidx.slidingpanelayout.demo.R
+
+class ItemAdapter(private val items: List<DemoItem<*>>) : RecyclerView.Adapter<ItemViewHolder>() {
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
+        val inflater = LayoutInflater.from(parent.context)
+        val rootView = inflater.inflate(R.layout.item_viewholder, parent, false)
+        return ItemViewHolder(rootView)
+    }
+
+    override fun getItemCount(): Int {
+        return items.size
+    }
+
+    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
+        holder.bind(items[position])
+    }
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemViewHolder.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemViewHolder.kt
new file mode 100644
index 0000000..2c48ee1
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemViewHolder.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 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.slidingpanelayout
+
+import android.content.Intent
+import android.view.View
+import android.widget.Button
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import androidx.slidingpanelayout.demo.R
+
+class ItemViewHolder(rootView: View) : RecyclerView.ViewHolder(rootView) {
+
+    private val titleTextView = rootView.findViewById<TextView>(R.id.title_textview)
+    private val descriptionTextView = rootView.findViewById<TextView>(R.id.description_textview)
+    private val launchButton = rootView.findViewById<Button>(R.id.button_launch)
+
+    fun bind(item: DemoItem<*>) {
+        titleTextView.text = item.title
+        descriptionTextView.text = item.description
+        launchButton.setOnClickListener { view ->
+            val context = view.context
+            val intent = Intent(context, item.clazz)
+            context.startActivity(intent)
+        }
+    }
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutDemos.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutDemos.kt
new file mode 100644
index 0000000..b6d4a1e1
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutDemos.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 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.slidingpanelayout
+
+import android.app.Activity
+import android.os.Bundle
+import androidx.recyclerview.widget.RecyclerView
+import androidx.slidingpanelayout.demo.R
+
+class SlidingPaneLayoutDemos : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_slidingpanelayout_demos)
+        val recyclerView = findViewById<RecyclerView>(R.id.demo_recyclerview)
+
+        recyclerView.adapter = ItemAdapter(
+            listOf(
+                DemoItem(
+                    SlidingPaneLayoutSample::class.java,
+                    "SlidingPaneLayoutSample",
+                    "Basic SlidingPaneLayoutSample"
+                )
+            )
+        )
+    }
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutSample.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutSample.kt
new file mode 100644
index 0000000..59463f5
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutSample.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 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.slidingpanelayout
+
+import android.app.Activity
+import android.os.Bundle
+
+class SlidingPaneLayoutSample : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(androidx.slidingpanelayout.demo.R.layout.activity_slidingpanelayout_sample)
+    }
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_demos.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_demos.xml
new file mode 100644
index 0000000..58bbd7b
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_demos.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:orientation="vertical"
+    android:id="@+id/demo_recyclerview"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
+
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_sample.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_sample.xml
new file mode 100644
index 0000000..436cc28
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_sample.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<androidx.slidingpanelayout.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <View
+        android:background="@android:color/holo_blue_light"
+        android:layout_width="200dp"
+        android:layout_height="match_parent"/>
+
+
+    <View
+        android:background="@android:color/holo_green_dark"
+        android:layout_width="200dp"
+        android:layout_weight="1"
+        android:layout_height="match_parent"/>
+
+</androidx.slidingpanelayout.widget.SlidingPaneLayout>
\ No newline at end of file
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/item_viewholder.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/item_viewholder.xml
new file mode 100644
index 0000000..631c063
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/item_viewholder.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2023 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"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <TextView
+        android:id="@+id/title_textview"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+
+    <TextView
+        android:id="@+id/description_textview"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <Button
+        android:id="@+id/button_launch"
+        android:text="@string/launch"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/res/values/strings.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/values/strings.xml
new file mode 100644
index 0000000..cbe5256
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2023 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.
+  -->
+
+<resources>
+    <string name="launch">Launch</string>
+</resources>
\ No newline at end of file
diff --git a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlTasks.kt b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlTasks.kt
index a8c0775..93bcbf8 100644
--- a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlTasks.kt
+++ b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlTasks.kt
@@ -24,7 +24,6 @@
 import com.android.build.api.variant.SourceDirectories
 import com.android.build.api.variant.Variant
 import com.android.utils.usLocaleCapitalize
-import java.io.File
 import org.gradle.api.Action
 import org.gradle.api.DomainObjectCollection
 import org.gradle.api.Project
@@ -36,6 +35,7 @@
 import org.gradle.api.file.FileCollection
 import org.gradle.api.file.RegularFile
 import org.gradle.api.provider.Provider
+import org.gradle.api.tasks.TaskOutputFilePropertyBuilder
 import org.gradle.api.tasks.TaskProvider
 
 // Gradle task group used to identify Stable AIDL tasks.
@@ -68,7 +68,6 @@
     }
 }
 
-@Suppress("UnstableApiUsage") // SourceDirectories.Flat
 fun registerCompileAidlApi(
     project: Project,
     variant: Variant,
@@ -277,18 +276,16 @@
 /**
  * Tells Gradle to skip running this task, even if this task declares no output files.
  */
-private fun Task.cacheEvenIfNoOutputs() {
-    this.outputs.file(this.getPlaceholderOutput())
-}
+private fun Task.cacheEvenIfNoOutputs(): TaskOutputFilePropertyBuilder =
+    outputs.file(getPlaceholderOutput())
 
 /**
  * Returns an unused output path that we can pass to Gradle to prevent Gradle from thinking that we
  * forgot to declare outputs of this task, and instead to skip this task if its inputs are
  * unchanged.
  */
-private fun Task.getPlaceholderOutput(): File {
-    return File(this.project.buildDir, "placeholderOutput/" + this.name.replace(":", "-"))
-}
+private fun Task.getPlaceholderOutput(): Provider<RegularFile> =
+    project.layout.buildDirectory.file("placeholderOutput/${name.replace(':', '-')}")
 
 private fun computeTaskName(prefix: String, variant: Variant, suffix: String) =
     "$prefix${variant.name.usLocaleCapitalize()}$suffix"
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Configurator.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Configurator.java
index 6b7931d..ad68c56 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Configurator.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Configurator.java
@@ -224,6 +224,11 @@
     /**
      * Sets the current tool type to use for motion events.
      * @see MotionEvent#getToolType(int)
+     * @see MotionEvent#TOOL_TYPE_FINGER
+     * @see MotionEvent#TOOL_TYPE_STYLUS
+     * @see MotionEvent#TOOL_TYPE_MOUSE
+     * @see MotionEvent#TOOL_TYPE_ERASER
+     * @see MotionEvent#TOOL_TYPE_UNKNOWN
      */
     public @NonNull Configurator setToolType(final int toolType) {
         mToolType = toolType;
diff --git a/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt b/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt
index 236b362..844f968 100644
--- a/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt
+++ b/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt
@@ -33,6 +33,7 @@
  *
  * See [ParameterizedHelperTest] for more examples.
  */
+// TODO(kuanyingchou): Remove and replace with TestParameterInjector"
 fun generateAllEnumerations(vararg args: List<Any>): List<Array<Any>> =
     generateAllEnumerationsIteratively(args.toList()).map { it.toTypedArray() }
 
diff --git a/transition/transition/api/current.txt b/transition/transition/api/current.txt
index 8b6b763..b6bf3ae 100644
--- a/transition/transition/api/current.txt
+++ b/transition/transition/api/current.txt
@@ -209,10 +209,10 @@
     method public static void beginDelayedTransition(android.view.ViewGroup);
     method public static void beginDelayedTransition(android.view.ViewGroup, androidx.transition.Transition?);
     method public static androidx.transition.TransitionSeekController? controlDelayedTransition(android.view.ViewGroup, androidx.transition.Transition);
+    method public static androidx.transition.TransitionSeekController? createSeekController(androidx.transition.Scene, androidx.transition.Transition);
     method public static void endTransitions(android.view.ViewGroup?);
     method public static void go(androidx.transition.Scene);
     method public static void go(androidx.transition.Scene, androidx.transition.Transition?);
-    method public static androidx.transition.TransitionSeekController? seekTo(androidx.transition.Scene, androidx.transition.Transition);
     method public void setTransition(androidx.transition.Scene, androidx.transition.Scene, androidx.transition.Transition?);
     method public void setTransition(androidx.transition.Scene, androidx.transition.Transition?);
     method public void transitionTo(androidx.transition.Scene);
diff --git a/transition/transition/api/restricted_current.txt b/transition/transition/api/restricted_current.txt
index bd19de1..3f44ab2 100644
--- a/transition/transition/api/restricted_current.txt
+++ b/transition/transition/api/restricted_current.txt
@@ -242,10 +242,10 @@
     method public static void beginDelayedTransition(android.view.ViewGroup);
     method public static void beginDelayedTransition(android.view.ViewGroup, androidx.transition.Transition?);
     method public static androidx.transition.TransitionSeekController? controlDelayedTransition(android.view.ViewGroup, androidx.transition.Transition);
+    method public static androidx.transition.TransitionSeekController? createSeekController(androidx.transition.Scene, androidx.transition.Transition);
     method public static void endTransitions(android.view.ViewGroup?);
     method public static void go(androidx.transition.Scene);
     method public static void go(androidx.transition.Scene, androidx.transition.Transition?);
-    method public static androidx.transition.TransitionSeekController? seekTo(androidx.transition.Scene, androidx.transition.Transition);
     method public void setTransition(androidx.transition.Scene, androidx.transition.Scene, androidx.transition.Transition?);
     method public void setTransition(androidx.transition.Scene, androidx.transition.Transition?);
     method public void transitionTo(androidx.transition.Scene);
diff --git a/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt b/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
index 852db4e..fd55d1a 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
+++ b/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
@@ -1060,7 +1060,7 @@
         }
 
         rule.runOnUiThread {
-            val controller = TransitionManager.seekTo(scene2, Fade())
+            val controller = TransitionManager.createSeekController(scene2, Fade())
             assertThat(controller).isNotNull()
             seekController = controller!!
         }
@@ -1096,7 +1096,7 @@
         }
 
         rule.runOnUiThread {
-            TransitionManager.seekTo(scene2, NoSeekingTransition())
+            TransitionManager.createSeekController(scene2, NoSeekingTransition())
         }
     }
 
@@ -1111,7 +1111,7 @@
 
         rule.runOnUiThread {
             TransitionManager.go(scene2, Fade())
-            assertThat(TransitionManager.seekTo(scene1, Fade())).isNull()
+            assertThat(TransitionManager.createSeekController(scene1, Fade())).isNull()
         }
     }
 }
diff --git a/transition/transition/src/main/java/androidx/transition/TransitionManager.java b/transition/transition/src/main/java/androidx/transition/TransitionManager.java
index 2fdb6d5..261738f4 100644
--- a/transition/transition/src/main/java/androidx/transition/TransitionManager.java
+++ b/transition/transition/src/main/java/androidx/transition/TransitionManager.java
@@ -365,7 +365,7 @@
      * {@link Transition#isSeekingSupported()}.
      */
     @Nullable
-    public static TransitionSeekController seekTo(
+    public static TransitionSeekController createSeekController(
             @NonNull Scene scene,
             @NonNull Transition transition
     ) {
diff --git a/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java b/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java
index f9b00c5..9e4f228 100644
--- a/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java
+++ b/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java
@@ -44,7 +44,7 @@
     long getCurrentPlayTimeMillis();
 
     /**
-     * @return The fraction, between 0 and 1, of the progress of the transition.
+     * @return The fraction, between 0 and 1, inclusive, of the progress of the transition.
      * @see #getCurrentPlayTimeMillis()
      */
     @FloatRange(from = 0.0, to = 1.0)
@@ -132,7 +132,7 @@
     void addOnProgressChangedListener(@NonNull Consumer<TransitionSeekController> consumer);
 
     /**
-     * Remove a listener previously added in {@link #addOnProgressChangedListener(Consumer)}\
+     * Remove a listener previously added in {@link #addOnProgressChangedListener(Consumer)}
      * @param consumer The listener to be removed.
      */
     void removeOnProgressChangedListener(@NonNull Consumer<TransitionSeekController> consumer);
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
index c0832bb..2546e1c 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
@@ -645,7 +645,7 @@
 @get:ExperimentalTvMaterial3Api
 val LocalAbsoluteTonalElevation = compositionLocalOf { 0.dp }
 
-private val AcceptableKeys = hashSetOf(
+private val AcceptableKeys = intArrayOf(
     NativeKeyEvent.KEYCODE_DPAD_CENTER,
     NativeKeyEvent.KEYCODE_ENTER,
     NativeKeyEvent.KEYCODE_NUMPAD_ENTER
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt
index e389f5c..9284438 100644
--- a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt
@@ -34,6 +34,8 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.Dp
 
 /**
@@ -80,14 +82,16 @@
             .clickable(
                 >
                 enabled = enabled,
-                role = Role.Button,
+                role = null, // provide the role via Modifier.semantics
                 interactionSource = interactionSource,
                 indication = rememberRipple(),
             )
             .then(
                 // Make sure modifier ordering is clip > clickable > padding > size,
                 // so that the ripple applies to the entire button shape and size.
+                // Then, apply semantics to apply the default semantic role (can be overridden)
                 modifier
+                    .semantics { role = Role.Button }
             )
             .size(buttonSize)
             .clip(shape) // Clip for the painted background area after size has been applied.
diff --git a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt
index 84db9176..ae4e264 100644
--- a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt
+++ b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt
@@ -767,11 +767,14 @@
     setContentWithTheme {
         background = MaterialTheme.colors.surface
         buttonColor = MaterialTheme.colors.primary
-        content(
-            Modifier
-                .testTag(TEST_TAG)
-                .padding(padding)
-                .background(background))
+        Box(Modifier.background(background)) {
+            content(
+                Modifier
+                    .testTag(TEST_TAG)
+                    .padding(padding)
+                    .background(background)
+            )
+        }
     }
 
     onNodeWithTag(TEST_TAG)
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleButton.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleButton.kt
index 812ac3d..4a208e3 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleButton.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleButton.kt
@@ -15,28 +15,22 @@
  */
 package androidx.wear.compose.material
 
-import androidx.compose.foundation.background
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.foundation.layout.defaultMinSize
-import androidx.compose.foundation.selection.toggleable
 import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
 
 /**
@@ -219,36 +213,24 @@
     role: Role = ToggleButtonDefaults.DefaultRole,
     content: @Composable BoxScope.() -> Unit,
 ) {
-    Box(
-        contentAlignment = Alignment.Center,
-        modifier = modifier
-            .defaultMinSize(
-                minWidth = ToggleButtonDefaults.DefaultToggleButtonSize,
-                minHeight = ToggleButtonDefaults.DefaultToggleButtonSize
-            )
-            .clip(shape)
-            .toggleable(
-                value = checked,
-                >
-                enabled = enabled,
-                role = role,
-                interactionSource = interactionSource,
-                indication = rememberRipple()
-            )
-            .background(
-                color = colors.backgroundColor(enabled = enabled, checked = checked).value,
-                shape = shape
-            )
-    ) {
-        val contentColor = colors.contentColor(enabled = enabled, checked = checked).value
-        CompositionLocalProvider(
-            LocalContentColor provides contentColor,
-            LocalContentAlpha provides contentColor.alpha,
-            LocalTextStyle provides MaterialTheme.typography.button,
-        ) {
-            content()
-        }
-    }
+    androidx.wear.compose.materialcore.ToggleButton(
+        checked = checked,
+        >
+        modifier = modifier.semantics { this.role = role },
+        enabled = enabled,
+        backgroundColor = { isEnabled, isChecked ->
+            colors.backgroundColor(enabled = isEnabled, checked = isChecked)
+        },
+        border = { _, _ -> null },
+        toggleButtonSize = ToggleButtonDefaults.DefaultToggleButtonSize,
+        interactionSource = interactionSource,
+        shape = shape,
+        content = provideScopeContent(
+            colors.contentColor(enabled = enabled, checked = checked),
+            MaterialTheme.typography.button,
+            content
+        )
+    )
 }
 /**
  * Represents the background and content colors used in a toggle button in different states.
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
index 76cddf8..d2c396f 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
@@ -29,6 +29,7 @@
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
 import androidx.wear.compose.material3.Button
+import androidx.wear.compose.material3.ButtonColors
 import androidx.wear.compose.material3.ButtonDefaults
 import androidx.wear.compose.material3.ChildButton
 import androidx.wear.compose.material3.FilledTonalButton
@@ -310,13 +311,17 @@
 @Composable
 private fun AvatarButton(enabled: Boolean) =
     MultilineButton(
-        enabled = enabled, icon = { AvatarIcon() }, label = { Text("Primary text") }
+        enabled = enabled,
+        colors = ButtonDefaults.filledTonalButtonColors(),
+        icon = { AvatarIcon() },
+        label = { Text("Primary text") }
     )
 
 @Composable
 private fun Avatar3SlotButton(enabled: Boolean) =
     Multiline3SlotButton(
         enabled = enabled,
+        colors = ButtonDefaults.filledTonalButtonColors(),
         icon = { AvatarIcon() },
         label = { Text("Primary text") },
         secondaryLabel = { Text("Secondary label") }
@@ -325,6 +330,7 @@
 @Composable
 private fun MultilineButton(
     enabled: Boolean,
+    colors: ButtonColors = ButtonDefaults.filledButtonColors(),
     icon: (@Composable BoxScope.() -> Unit)? = null,
     label: @Composable RowScope.() -> Unit = {
         Text(
@@ -338,13 +344,15 @@
          /* Do something */ },
         icon = icon,
         label = label,
-        enabled = enabled
+        enabled = enabled,
+        colors = colors,
     )
 }
 
 @Composable
 private fun Multiline3SlotButton(
     enabled: Boolean,
+    colors: ButtonColors = ButtonDefaults.filledButtonColors(),
     icon: (@Composable BoxScope.() -> Unit)? = null,
     label: @Composable RowScope.() -> Unit = {
         Text(
@@ -366,7 +374,8 @@
         icon = icon,
         label = label,
         secondaryLabel = secondaryLabel,
-        enabled = enabled
+        enabled = enabled,
+        colors = colors,
     )
 }
 
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
index eb4b7c0..a5da87e 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
@@ -386,7 +386,7 @@
             status = Status.Disabled,
             colors = { ButtonDefaults.filledButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
+                alpha = DisabledContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -412,7 +412,7 @@
             status = Status.Disabled,
             colors = { ButtonDefaults.filledTonalButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
+                alpha = DisabledContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -461,9 +461,7 @@
         rule.verifyButtonColors(
             status = Status.Disabled,
             colors = { ButtonDefaults.childButtonColors() },
-            expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
-            ) },
+            expectedContainerColor = { Color.Transparent },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
             ) },
@@ -563,7 +561,9 @@
     fun gives_disabled_outlined_button_correct_border_colors() {
         val status = Status.Disabled
         rule.verifyButtonBorderColor(
-            expectedBorderColor = { MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f) },
+            expectedBorderColor = {
+                MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAlpha)
+            },
             content = { modifier: Modifier ->
                 OutlinedButton(
                     >
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt
index e1bcf92..07d2ceb 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt
@@ -70,7 +70,7 @@
     }
 
     @Test
-    fun filled_icon_button_disabled() = verifyScreenshot("icon_button_disabled") {
+    fun filled_icon_button_disabled() = verifyScreenshot {
         sampleFilledIconButton(enabled = false, isCompact = false)
     }
 
@@ -80,7 +80,7 @@
     }
 
     @Test
-    fun filled_tonal_icon_button_disabled() = verifyScreenshot("icon_button_disabled") {
+    fun filled_tonal_icon_button_disabled() = verifyScreenshot {
         sampleFilledTonalIconButton(enabled = false, isCompact = false)
     }
 
@@ -110,7 +110,7 @@
     }
 
     @Test
-    fun filled_compact_icon_button_disabled() = verifyScreenshot("compact_icon_button_disabled") {
+    fun filled_compact_icon_button_disabled() = verifyScreenshot {
         sampleFilledIconButton(enabled = false, isCompact = true)
     }
 
@@ -121,7 +121,7 @@
 
     @Test
     fun filled_tonal_compact_icon_button_disabled() =
-        verifyScreenshot("compact_icon_button_disabled") {
+        verifyScreenshot {
             sampleFilledTonalIconButton(enabled = false, isCompact = true)
         }
 
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
index d12eb43..f6f9e3b 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
@@ -31,6 +31,8 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertHasClickAction
@@ -191,6 +193,27 @@
     }
 
     @Test
+    fun allows_custom_role() {
+        val overrideRole = Role.Checkbox
+
+        rule.setContentWithTheme {
+            IconButton(
+                >
+                modifier = Modifier.testTag(TEST_TAG).semantics { role = overrideRole }
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assert(
+            SemanticsMatcher.expectValue(
+                SemanticsProperties.Role,
+                overrideRole
+            )
+        )
+    }
+
+    @Test
     fun gives_default_button_correct_tap_size() {
         rule.verifyTapSize(DefaultButtonSize) { modifier ->
             IconButton(
@@ -317,7 +340,7 @@
             status = Status.Disabled,
             colors = { IconButtonDefaults.filledIconButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
+                alpha = DisabledContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -343,7 +366,7 @@
             status = Status.Disabled,
             colors = { IconButtonDefaults.filledTonalIconButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
+                alpha = DisabledContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -397,7 +420,7 @@
         val status = Status.Disabled
         rule.verifyButtonBorderColor(
             expectedBorderColor = {
-                MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAndContainerAlpha)
+                MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAlpha)
             },
             content = { modifier: Modifier ->
                 OutlinedIconButton(
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt
index 967714f3..11b0b55 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt
@@ -66,7 +66,7 @@
 
     @Test
     fun filled_text_button_disabled() =
-        verifyScreenshot("text_button_disabled") {
+        verifyScreenshot {
             sampleFilledTextButton(enabled = false, isCompact = false)
         }
 
@@ -77,7 +77,7 @@
 
     @Test
     fun filled_tonal_text_button_disabled() =
-        verifyScreenshot("text_button_disabled") {
+        verifyScreenshot {
             sampleFilledTonalTextButton(enabled = false, isCompact = false)
         }
 
@@ -108,7 +108,7 @@
 
     @Test
     fun filled_compact_text_button_disabled() =
-        verifyScreenshot("compact_text_button_disabled") {
+        verifyScreenshot {
             sampleFilledTextButton(enabled = false, isCompact = true)
         }
 
@@ -119,7 +119,7 @@
 
     @Test
     fun filled_tonal_compact_text_button_disabled() =
-        verifyScreenshot("compact_text_button_disabled") {
+        verifyScreenshot {
             sampleFilledTonalTextButton(enabled = false, isCompact = true)
         }
 
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
index a37ef7b..f6a0b7b 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
@@ -31,6 +31,8 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertHasClickAction
@@ -222,6 +224,27 @@
     }
 
     @Test
+    fun allows_custom_role() {
+        val overrideRole = Role.Checkbox
+
+        rule.setContentWithTheme {
+            TextButton(
+                >
+                modifier = Modifier.testTag(TEST_TAG).semantics { role = overrideRole }
+            ) {
+                Text("Test")
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assert(
+            SemanticsMatcher.expectValue(
+                SemanticsProperties.Role,
+                overrideRole
+            )
+        )
+    }
+
+    @Test
     fun sets_correct_font() {
         var actualTextStyle = TextStyle.Default
         var expectedTextStyle = TextStyle.Default
@@ -365,7 +388,7 @@
             status = Status.Disabled,
             colors = { TextButtonDefaults.filledTextButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
+                alpha = DisabledContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -391,7 +414,7 @@
             status = Status.Disabled,
             colors = { TextButtonDefaults.filledTonalTextButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
+                alpha = DisabledContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -416,9 +439,7 @@
         rule.verifyTextButtonColors(
             status = Status.Disabled,
             colors = { TextButtonDefaults.outlinedTextButtonColors() },
-            expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
-            ) },
+            expectedContainerColor = { Color.Transparent },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
             ) }
@@ -449,7 +470,7 @@
         val status = Status.Disabled
         rule.verifyButtonBorderColor(
             expectedBorderColor = {
-                MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAndContainerAlpha)
+                MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAlpha)
             },
             content = { modifier: Modifier ->
                 TextButton(
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
index 6c8de9eb59..e608707 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
@@ -715,7 +715,7 @@
         containerColor: Color = MaterialTheme.colorScheme.surface,
         contentColor: Color = MaterialTheme.colorScheme.onSurface,
         secondaryContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
-        iconColor: Color = MaterialTheme.colorScheme.onSurface
+        iconColor: Color = MaterialTheme.colorScheme.primary,
     ): ButtonColors {
         return buttonColors(
             containerColor = containerColor,
@@ -742,13 +742,14 @@
     fun outlinedButtonColors(
         contentColor: Color = MaterialTheme.colorScheme.onSurface,
         secondaryContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
-        iconColor: Color = MaterialTheme.colorScheme.onSurface
+        iconColor: Color = MaterialTheme.colorScheme.primary,
     ): ButtonColors {
         return buttonColors(
             containerColor = Color.Transparent,
             contentColor = contentColor,
             secondaryContentColor = secondaryContentColor,
-            iconColor = iconColor
+            iconColor = iconColor,
+            disabledContainerColor = Color.Transparent,
         )
     }
 
@@ -769,13 +770,14 @@
     fun childButtonColors(
         contentColor: Color = MaterialTheme.colorScheme.onSurface,
         secondaryContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
-        iconColor: Color = MaterialTheme.colorScheme.onSurface
+        iconColor: Color = MaterialTheme.colorScheme.primary
     ): ButtonColors {
         return buttonColors(
             containerColor = Color.Transparent,
             contentColor = contentColor,
             secondaryContentColor = secondaryContentColor,
-            iconColor = iconColor
+            iconColor = iconColor,
+            disabledContainerColor = Color.Transparent,
         )
     }
 
@@ -851,7 +853,7 @@
         enabled: Boolean,
         borderColor: Color = MaterialTheme.colorScheme.outline,
         disabledBorderColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledBorderAlpha
         ),
         borderWidth: Dp = 1.dp
     ): BorderStroke {
@@ -901,7 +903,7 @@
         secondaryContentColor: Color = contentColor,
         iconColor: Color = contentColor,
         disabledContainerColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledContainerAlpha
         ),
         disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(),
         disabledSecondaryContentColor: Color =
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt
index 8471524..b978a2b 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt
@@ -33,18 +33,18 @@
  * body text.
  *
  */
-public val LocalContentAlpha: ProvidableCompositionLocal<Float> = compositionLocalOf { 1f }
+val LocalContentAlpha: ProvidableCompositionLocal<Float> = compositionLocalOf { 1f }
 
 /**
  * Default alpha levels used by Material components.
  *
  * See [LocalContentAlpha].
  */
-public object ContentAlpha {
+object ContentAlpha {
     /**
      * A high level of content alpha, used to represent high emphasis text.
      */
-    public val high: Float
+    val high: Float
         @Composable
         get() = contentAlpha(
             highContrastAlpha = HighContrastContentAlpha.high,
@@ -55,7 +55,7 @@
      * A medium level of content alpha, used to represent medium emphasis text such as
      * placeholder text.
      */
-    public val medium: Float
+    val medium: Float
         @Composable
         get() = contentAlpha(
             highContrastAlpha = HighContrastContentAlpha.medium,
@@ -66,7 +66,7 @@
      * A low level of content alpha used to represent disabled components, such as text in a
      * disabled Button.
      */
-    public val disabled: Float
+    val disabled: Float
         @Composable
         get() = contentAlpha(
             highContrastAlpha = HighContrastContentAlpha.disabled,
@@ -123,4 +123,5 @@
     const val disabled: Float = 0.38f
 }
 
-internal const val DisabledBorderAndContainerAlpha = 0.12f
+internal const val DisabledContainerAlpha = 0.12f
+internal const val DisabledBorderAlpha = 0.20f
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
index 042f777..ff669b8 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
@@ -358,7 +358,10 @@
     ): IconButtonColors {
         return iconButtonColors(
             containerColor = containerColor,
-            contentColor = contentColor
+            contentColor = contentColor,
+            disabledContainerColor = MaterialTheme.colorScheme.onSurface.toDisabledColor(
+                disabledAlpha = DisabledContainerAlpha
+            ),
         )
     }
 
@@ -378,7 +381,10 @@
     ): IconButtonColors {
         return iconButtonColors(
             containerColor = containerColor,
-            contentColor = contentColor
+            contentColor = contentColor,
+            disabledContainerColor = MaterialTheme.colorScheme.onSurface.toDisabledColor(
+                disabledAlpha = DisabledContainerAlpha
+            ),
         )
     }
 
@@ -396,7 +402,7 @@
     ): IconButtonColors {
         return iconButtonColors(
             containerColor = Color.Transparent,
-            contentColor = contentColor
+            contentColor = contentColor,
         )
     }
 
@@ -415,9 +421,7 @@
     fun iconButtonColors(
         containerColor: Color = Color.Transparent,
         contentColor: Color = MaterialTheme.colorScheme.onBackground,
-        disabledContainerColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
-        ),
+        disabledContainerColor: Color = Color.Transparent,
         disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor()
     ): IconButtonColors = IconButtonColors(
         containerColor = containerColor,
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SelectionControls.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SelectionControls.kt
index adf549b..128b9a5 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SelectionControls.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SelectionControls.kt
@@ -491,11 +491,11 @@
         uncheckedBoxColor = uncheckedBoxColor,
         uncheckedCheckmarkColor = uncheckedCheckmarkColor,
         disabledCheckedBoxColor = checkedBoxColor.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledContainerAlpha
         ),
         disabledCheckedCheckmarkColor = checkedCheckmarkColor.toDisabledColor(),
         disabledUncheckedBoxColor = uncheckedBoxColor.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledContainerAlpha
         ),
         disabledUncheckedCheckmarkColor = uncheckedCheckmarkColor.toDisabledColor()
     )
@@ -539,18 +539,18 @@
         disabledCheckedThumbColor = checkedThumbColor.toDisabledColor(),
         disabledCheckedThumbIconColor = checkedThumbIconColor.toDisabledColor(),
         disabledCheckedTrackColor = checkedTrackColor.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledContainerAlpha
         ),
         disabledCheckedTrackBorderColor = checkedTrackStrokeColor.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledBorderAlpha
         ),
         disabledUncheckedThumbColor = uncheckedThumbColor.toDisabledColor(),
         disabledUncheckedThumbIconColor = uncheckedThumbIconColor.toDisabledColor(),
         disabledUncheckedTrackColor = uncheckedTrackColor.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledContainerAlpha
         ),
         disabledUncheckedTrackBorderColor = uncheckedTrackStrokeColor.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledBorderAlpha
         )
     )
 }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt
index d6a155f..9563288 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt
@@ -328,13 +328,13 @@
         unselectedBarColor: Color = MaterialTheme.colorScheme.background.copy(alpha = 0.3f),
         barSeparatorColor: Color = MaterialTheme.colorScheme.primaryDim,
         disabledContainerColor: Color = containerColor.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledContainerAlpha
         ),
         disabledButtonIconColor: Color = buttonIconColor.toDisabledColor(),
         disabledSelectedBarColor: Color = selectedBarColor.toDisabledColor(),
         disabledUnselectedBarColor: Color = unselectedBarColor.toDisabledColor(),
         disabledBarSeparatorColor: Color = barSeparatorColor.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledContainerAlpha
         )
     ): InlineSliderColors = InlineSliderColors(
         containerColor = containerColor,
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
index f3d2722..e0349a1 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
@@ -135,7 +135,7 @@
  * @param content The text to be drawn inside the toggle button.
  */
 @Composable
-public fun TextToggleButton(
+fun TextToggleButton(
     checked: Boolean,
     onCheckedChange: (Boolean) -> Unit,
     modifier: Modifier = Modifier,
@@ -194,7 +194,10 @@
     ): TextButtonColors {
         return textButtonColors(
             containerColor = containerColor,
-            contentColor = contentColor
+            contentColor = contentColor,
+            disabledContainerColor = MaterialTheme.colorScheme.onSurface.toDisabledColor(
+                disabledAlpha = DisabledContainerAlpha
+            ),
         )
     }
 
@@ -217,7 +220,10 @@
     ): TextButtonColors {
         return textButtonColors(
             containerColor = containerColor,
-            contentColor = contentColor
+            contentColor = contentColor,
+            disabledContainerColor = MaterialTheme.colorScheme.onSurface.toDisabledColor(
+                disabledAlpha = DisabledContainerAlpha
+            ),
         )
     }
 
@@ -256,9 +262,7 @@
     fun textButtonColors(
         containerColor: Color = Color.Transparent,
         contentColor: Color = MaterialTheme.colorScheme.onBackground,
-        disabledContainerColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
-        ),
+        disabledContainerColor: Color = Color.Transparent,
         disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor()
     ): TextButtonColors = TextButtonColors(
         containerColor = containerColor,
@@ -291,7 +295,7 @@
      * unchecked and not enabled
      */
     @Composable
-    public fun textToggleButtonColors(
+    fun textToggleButtonColors(
         checkedContainerColor: Color = MaterialTheme.colorScheme.primary,
         checkedContentColor: Color = MaterialTheme.colorScheme.onPrimary,
         uncheckedContainerColor: Color = MaterialTheme.colorScheme.surface,