Merge "Added configuration to explicit dependency on agp in baseline profile gradle plugin tests" into androidx-main
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityImplTest.kt
similarity index 100%
rename from appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityImplTest.kt
rename to appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityImplTest.kt
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityUtilsTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityUtilsTest.kt
similarity index 100%
rename from appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskCapabilityUtilsTest.kt
rename to appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskCapabilityUtilsTest.kt
diff --git a/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskSlotProcessorTest.kt b/appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskSlotProcessorTest.kt
similarity index 100%
rename from appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/task/impl/TaskSlotProcessorTest.kt
rename to appactions/interaction/interaction-capabilities-core/src/test/java/androidx/appactions/interaction/capabilities/core/impl/task/TaskSlotProcessorTest.kt
diff --git a/benchmark/benchmark-macro/build.gradle b/benchmark/benchmark-macro/build.gradle
index 0bb6d0b..538d68d 100644
--- a/benchmark/benchmark-macro/build.gradle
+++ b/benchmark/benchmark-macro/build.gradle
@@ -158,6 +158,13 @@
     }
 }
 
+androidx {
+    deviceTests {
+        targetAppProject = project(":benchmark:integration-tests:macrobenchmark-target")
+        targetAppVariant = "release"
+    }
+}
+
 // Define a task dependency so the app is installed before we run macro benchmarks.
 afterEvaluate {
     // `:benchmark:integration-tests:macrobenchmark-target:installRelease` is not in the compose
diff --git a/benchmark/integration-tests/baselineprofile-flavors-producer/build.gradle b/benchmark/integration-tests/baselineprofile-flavors-producer/build.gradle
index 0634f31..05d1c99 100644
--- a/benchmark/integration-tests/baselineprofile-flavors-producer/build.gradle
+++ b/benchmark/integration-tests/baselineprofile-flavors-producer/build.gradle
@@ -61,5 +61,7 @@
 }
 
 androidx {
-    disableDeviceTests = true
+    deviceTests {
+        enabled = false
+    }
 }
diff --git a/benchmark/integration-tests/baselineprofile-library-producer/build.gradle b/benchmark/integration-tests/baselineprofile-library-producer/build.gradle
index b6cee42..44b3bda 100644
--- a/benchmark/integration-tests/baselineprofile-library-producer/build.gradle
+++ b/benchmark/integration-tests/baselineprofile-library-producer/build.gradle
@@ -56,5 +56,7 @@
 }
 
 androidx {
-    disableDeviceTests = true
+    deviceTests {
+        enabled = false
+    }
 }
diff --git a/benchmark/integration-tests/baselineprofile-producer/build.gradle b/benchmark/integration-tests/baselineprofile-producer/build.gradle
index a696a90..e04a270 100644
--- a/benchmark/integration-tests/baselineprofile-producer/build.gradle
+++ b/benchmark/integration-tests/baselineprofile-producer/build.gradle
@@ -56,5 +56,7 @@
 }
 
 androidx {
-    disableDeviceTests = true
+    deviceTests {
+        enabled = false
+    }
 }
diff --git a/benchmark/integration-tests/macrobenchmark/build.gradle b/benchmark/integration-tests/macrobenchmark/build.gradle
index a62c063..58d7ee0 100644
--- a/benchmark/integration-tests/macrobenchmark/build.gradle
+++ b/benchmark/integration-tests/macrobenchmark/build.gradle
@@ -19,7 +19,7 @@
 
 plugins {
     id("AndroidXPlugin")
-    id("com.android.library")
+    id("com.android.test")
     id("kotlin-android")
 }
 
@@ -28,34 +28,31 @@
         minSdkVersion 23
     }
     sourceSets {
-        androidTest.assets.srcDirs += new File(
+        main.assets.srcDirs += new File(
                 SupportConfigKt.getPrebuiltsRoot(project),
                 "androidx/traceprocessor/testdata"
         )
     }
     namespace "androidx.benchmark.integration.macrobenchmark"
+    targetProjectPath = ":benchmark:integration-tests:macrobenchmark-target"
+    experimentalProperties["android.experimental.self-instrumenting"] = true
 }
 
+// Create a release build type and make sure it's the only one enabled.
+// This is needed because we benchmark the release build type only.
+android.buildTypes { release {} }
+androidComponents { beforeVariants(selector().all()) { enabled = buildType == 'release' } }
+
 dependencies {
-    androidTestImplementation(project(":benchmark:benchmark-junit4"))
-    androidTestImplementation(project(":benchmark:benchmark-macro-junit4"))
-    androidTestImplementation(project(":internal-testutils-macrobenchmark"))
-    androidTestImplementation(project(":tracing:tracing-ktx"))
-    androidTestImplementation(libs.kotlinTest)
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testUiautomator)
-    androidTestImplementation(libs.testExtTruth)
-}
-
-// Define a task dependency so the app is installed before we run macro benchmarks.
-afterEvaluate {
-    tasks.getByPath(":benchmark:integration-tests:macrobenchmark:connectedDebugAndroidTest")
-            .dependsOn(
-                    tasks.getByPath(
-                            ":benchmark:integration-tests:macrobenchmark-target:installRelease"
-                    )
-            )
+    implementation(project(":benchmark:benchmark-junit4"))
+    implementation(project(":benchmark:benchmark-macro-junit4"))
+    implementation(project(":internal-testutils-macrobenchmark"))
+    implementation(project(":tracing:tracing-ktx"))
+    implementation(libs.kotlinTest)
+    implementation(libs.testRules)
+    implementation(libs.testExtJunit)
+    implementation(libs.testCore)
+    implementation(libs.testRunner)
+    implementation(libs.testUiautomator)
+    implementation(libs.testExtTruth)
 }
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/AudioUnderrunBenchmark.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/AudioUnderrunBenchmark.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/AudioUnderrunBenchmark.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/AudioUnderrunBenchmark.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/BaselineProfileRuleTest.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/BaselineProfileRuleTest.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/BaselineProfileRuleTest.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/BaselineProfileRuleTest.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/CompilationModeTest.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/CompilationModeTest.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/CompilationModeTest.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/CompilationModeTest.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/GithubBrowserBaselineProfile.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/GithubBrowserBaselineProfile.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/GithubBrowserBaselineProfile.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/GithubBrowserBaselineProfile.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/GridBenchmark.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/GridBenchmark.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/GridBenchmark.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/GridBenchmark.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/PerfettoTraceProcessorBenchmark.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/PerfettoTraceProcessorBenchmark.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/PerfettoTraceProcessorBenchmark.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/PerfettoTraceProcessorBenchmark.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/SeparateProcessBenchmark.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/SeparateProcessBenchmark.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/SeparateProcessBenchmark.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/SeparateProcessBenchmark.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/SingleColorPowerBenchmark.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/SingleColorPowerBenchmark.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/SingleColorPowerBenchmark.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/SingleColorPowerBenchmark.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/SmallListStartupBenchmark.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/SmallListStartupBenchmark.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/SmallListStartupBenchmark.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/SmallListStartupBenchmark.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/StartupPressHomeBenchmark.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/StartupPressHomeBenchmark.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/StartupPressHomeBenchmark.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/StartupPressHomeBenchmark.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/SystemUiBenchmark.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/SystemUiBenchmark.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/SystemUiBenchmark.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/SystemUiBenchmark.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialListScrollBaselineProfile.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialListScrollBaselineProfile.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialListScrollBaselineProfile.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialListScrollBaselineProfile.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialListScrollBenchmark.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialListScrollBenchmark.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialListScrollBenchmark.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialListScrollBenchmark.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialPowerBenchmark.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialPowerBenchmark.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialPowerBenchmark.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialPowerBenchmark.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupBaselineProfile.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupBaselineProfile.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupBaselineProfile.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupBaselineProfile.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupBenchmark.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupBenchmark.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupBenchmark.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupBenchmark.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupFullyDrawnBenchmark.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupFullyDrawnBenchmark.kt
similarity index 100%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupFullyDrawnBenchmark.kt
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupFullyDrawnBenchmark.kt
diff --git a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupJavaBenchmark.java b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupJavaBenchmark.java
similarity index 92%
rename from benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupJavaBenchmark.java
rename to benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupJavaBenchmark.java
index 2e75f3a..8090aa3 100644
--- a/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupJavaBenchmark.java
+++ b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialStartupJavaBenchmark.java
@@ -20,19 +20,27 @@
 import androidx.benchmark.macro.StartupMode;
 import androidx.benchmark.macro.StartupTimingMetric;
 import androidx.benchmark.macro.junit4.MacrobenchmarkRule;
+import androidx.test.filters.LargeTest;
 import androidx.test.filters.SdkSuppress;
 
+import kotlin.Unit;
+
 import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.Collections;
 
-import kotlin.Unit;
-
+/**
+ * Simple benchmark for startup in java.
+ */
+@LargeTest
 public class TrivialStartupJavaBenchmark {
     @Rule
     public MacrobenchmarkRule mBenchmarkRule = new MacrobenchmarkRule();
 
+    /**
+     * Benchmark for startup.
+     */
     @Test
     @SdkSuppress(minSdkVersion = 29)
     public void startup() {
diff --git a/benchmark/integration-tests/test-module-sample/build.gradle b/benchmark/integration-tests/test-module-sample/build.gradle
deleted file mode 100644
index 5351938..0000000
--- a/benchmark/integration-tests/test-module-sample/build.gradle
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-plugins {
-    id("AndroidXPlugin")
-    id("com.android.test")
-    id("kotlin-android")
-}
-
-android {
-    targetProjectPath = ":benchmark:integration-tests:macrobenchmark-target"
-    experimentalProperties["android.experimental.self-instrumenting"] = true
-
-    // note: below is optional, mimicing eventual benchmark module setup
-    buildTypes {
-        release {
-            debuggable = true
-        }
-    }
-
-    defaultConfig {
-        minSdkVersion 28
-    }
-    namespace "androidx.benchmark.integration.testmodulesample"
-}
-
-// note: below is optional, mimicing eventual benchmark module setup
-androidComponents {
-    beforeVariants(selector().all()) {
-        // Enable only the release buildType, since we only want to measure
-        // release build performance
-        getLogger().info("setting enable for variant" + name + ", buildType " + buildType)
-        enabled = buildType == 'release'
-    }
-}
-
-dependencies {
-    implementation(libs.kotlinStdlib)
-    implementation(libs.testRules)
-    implementation(libs.testExtJunit)
-    implementation(libs.testCore)
-    implementation(libs.testRunner)
-}
diff --git a/benchmark/integration-tests/test-module-sample/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialTestModuleTest.kt b/benchmark/integration-tests/test-module-sample/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialTestModuleTest.kt
deleted file mode 100644
index aac7497..0000000
--- a/benchmark/integration-tests/test-module-sample/src/main/java/androidx/benchmark/integration/macrobenchmark/TrivialTestModuleTest.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.benchmark.integration.macrobenchmark
-
-import android.content.pm.PackageManager
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.LargeTest
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.Assert.assertEquals
-import org.junit.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@LargeTest
-@RunWith(AndroidJUnit4::class)
-class TrivialTestModuleTest {
-    @Test
-    fun targetPackage() {
-        // if self-instrumenting wasn't used, this would instrument the target app,
-        // and we'd see TargetPackage instead
-        assertEquals(
-            TestPackage,
-            InstrumentationRegistry.getInstrumentation().targetContext.packageName
-        )
-    }
-
-    @Test
-    fun testPackage() {
-        assertEquals(
-            TestPackage,
-            InstrumentationRegistry.getInstrumentation().context.packageName
-        )
-    }
-
-    @Ignore // b/202321897
-    @Test
-    @Suppress("DEPRECATION")
-    fun targetPackageInstalled() {
-        val pm = InstrumentationRegistry.getInstrumentation().context.packageManager
-        try {
-            pm.getApplicationInfo(TargetPackage, 0)
-        } catch (notFoundException: PackageManager.NameNotFoundException) {
-            throw AssertionError(
-                "Unable to find target package $TargetPackage, is it installed?",
-                notFoundException
-            )
-        }
-    }
-
-    companion object {
-        const val TargetPackage = "androidx.benchmark.integration.macrobenchmark.target"
-        const val TestPackage = "androidx.benchmark.integration.testmodulesample"
-    }
-}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXExtension.kt
index 8c0a117..242b964 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXExtension.kt
@@ -23,13 +23,16 @@
 import java.io.File
 import org.gradle.api.GradleException
 import org.gradle.api.Project
+import org.gradle.api.plugins.ExtensionAware
+import org.gradle.api.plugins.ExtensionContainer
 import org.gradle.api.provider.Property
 import org.gradle.api.provider.Provider
 
 /**
  * Extension for [AndroidXImplPlugin] that's responsible for holding configuration options.
  */
-open class AndroidXExtension(val project: Project) {
+abstract class AndroidXExtension(val project: Project) : ExtensionAware {
+
     @JvmField
     val LibraryVersions: Map<String, Version>
 
@@ -45,6 +48,8 @@
 
     private val versionService: LibraryVersionsService
 
+    val deviceTests = DeviceTests.register(project.extensions)
+
     init {
         val tomlFileName = "libraryversions.toml"
         val toml = lazyReadFile(tomlFileName)
@@ -376,8 +381,6 @@
 
     var metalavaK2UastEnabled = false
 
-    var disableDeviceTests = false
-
     val additionalDeviceTestApkKeys = mutableListOf<String>()
 
     val additionalDeviceTestTags: MutableList<String> by lazy {
@@ -422,3 +425,18 @@
     var name: String? = null
     var url: String? = null
 }
+
+abstract class DeviceTests {
+
+    companion object {
+        private const val EXTENSION_NAME = "deviceTests"
+        internal fun register(extensions: ExtensionContainer): DeviceTests {
+            return extensions.findByType(DeviceTests::class.java)
+                ?: extensions.create(EXTENSION_NAME, DeviceTests::class.java)
+        }
+    }
+
+    var enabled = true
+    var targetAppProject: Project? = null
+    var targetAppVariant = "debug"
+}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index 3fae48c..b8b224c 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -323,7 +323,7 @@
     private fun configureWithAppPlugin(project: Project, androidXExtension: AndroidXExtension) {
         project.extensions.getByType<AppExtension>().apply {
             configureAndroidBaseOptions(project, androidXExtension)
-            configureAndroidApplicationOptions(project)
+            configureAndroidApplicationOptions(project, androidXExtension)
         }
 
         project.extensions.getByType<ApplicationAndroidComponentsExtension>().apply {
@@ -347,6 +347,7 @@
     ) {
         project.extensions.getByType<TestExtension>().apply {
             configureAndroidBaseOptions(project, androidXExtension)
+            project.addAppApkToTestConfigGeneration(androidXExtension)
         }
 
         project.configureJavaCompilationWarnings(androidXExtension)
@@ -410,6 +411,7 @@
     ) {
         val libraryExtension = project.extensions.getByType<LibraryExtension>().apply {
             configureAndroidBaseOptions(project, androidXExtension)
+            project.addAppApkToTestConfigGeneration(androidXExtension)
             configureAndroidLibraryOptions(project, androidXExtension)
 
             // Make sure the main Kotlin source set doesn't contain anything under src/main/kotlin.
@@ -808,13 +810,16 @@
         }
     }
 
-    private fun AppExtension.configureAndroidApplicationOptions(project: Project) {
+    private fun AppExtension.configureAndroidApplicationOptions(
+        project: Project,
+        androidXExtension: AndroidXExtension
+    ) {
         defaultConfig.apply {
             versionCode = 1
             versionName = "1.0"
         }
 
-        project.addAppApkToTestConfigGeneration()
+        project.addAppApkToTestConfigGeneration(androidXExtension)
         project.addAppApkToFtlRunner()
     }
 
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 1960148..3f8005e 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
@@ -19,10 +19,13 @@
 import androidx.build.dependencyTracker.ProjectSubset
 import com.android.build.api.variant.BuiltArtifactsLoader
 import java.io.File
+import javax.inject.Inject
 import org.gradle.api.DefaultTask
 import org.gradle.api.GradleException
+import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.file.DirectoryProperty
 import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.model.ObjectFactory
 import org.gradle.api.provider.ListProperty
 import org.gradle.api.provider.Property
 import org.gradle.api.tasks.Input
@@ -42,13 +45,19 @@
  * This config gets ingested by Tradefed.
  */
 @DisableCachingByDefault(because = "Doesn't benefit from caching")
-abstract class GenerateTestConfigurationTask : DefaultTask() {
+abstract class GenerateTestConfigurationTask @Inject constructor(
+    private val objects: ObjectFactory
+) : DefaultTask() {
 
     @get:InputFiles
     @get:Optional
     @get:PathSensitive(PathSensitivity.RELATIVE)
     abstract val appFolder: DirectoryProperty
 
+    @get:InputFiles
+    @get:PathSensitive(PathSensitivity.RELATIVE)
+    abstract val appFileCollection: ConfigurableFileCollection
+
     @get:Internal
     abstract val appLoader: Property<BuiltArtifactsLoader>
 
@@ -129,7 +138,20 @@
         val configBuilder = ConfigBuilder()
         configBuilder.configName = outputFile.asFile.get().name
         if (appLoader.isPresent) {
-            val appApk = appLoader.get().load(appFolder.get())
+
+            // Decides where to load the app apk from, depending on whether appFolder or
+            // appFileCollection has been set.
+            val appDir = if (appFolder.isPresent && appFileCollection.files.isEmpty()) {
+                appFolder.get()
+            } else if (!appFolder.isPresent && appFileCollection.files.size == 1) {
+                objects.directoryProperty().also { it.set(appFileCollection.files.first()) }.get()
+            } else {
+                throw IllegalStateException("""
+                    App apk not specified or both appFileCollection and appFolder specified.
+                """.trimIndent())
+            }
+
+            val appApk = appLoader.get().load(appDir)
                 ?: throw RuntimeException("Cannot load required APK for task: $name")
             // We don't need to check hasBenchmarkPlugin because benchmarks shouldn't have test apps
             val appApkBuiltArtifact = appApk.elements.single()
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
index 8ca7eb7..152b49a 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
@@ -35,27 +35,31 @@
 import com.android.build.api.variant.AndroidComponentsExtension
 import com.android.build.api.variant.ApplicationAndroidComponentsExtension
 import com.android.build.api.variant.HasAndroidTest
+import com.android.build.api.variant.LibraryAndroidComponentsExtension
+import com.android.build.api.variant.TestAndroidComponentsExtension
+import com.android.build.api.variant.Variant
 import com.android.build.gradle.BaseExtension
 import java.io.File
 import org.gradle.api.Project
-import org.gradle.api.UnknownTaskException
+import com.android.build.gradle.TestExtension
+import com.android.build.gradle.internal.attributes.VariantAttr
+import com.android.build.gradle.internal.publishing.AndroidArtifacts
+import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType
+import org.gradle.api.attributes.Usage
 import org.gradle.api.tasks.TaskProvider
 import org.gradle.kotlin.dsl.getByType
+import org.gradle.kotlin.dsl.named
 
 /**
  * Creates and configures the test config generation task for a project. Configuration includes
  * populating the task with relevant data from the first 4 params, and setting whether the task
  * is enabled.
- *
- * @param overrideProject Allows the config task for one project to get registered to an
- * alternative project. Default is for the project to register the new config task to itself
  */
 fun Project.createTestConfigurationGenerationTask(
     variantName: String,
     artifacts: Artifacts,
     minSdk: Int,
     testRunner: String,
-    overrideProject: Project = this
 ) {
     val xmlName = "${path.asFilenamePrefix()}$variantName.xml"
     val jsonName = "_${path.asFilenamePrefix()}$variantName.json"
@@ -68,7 +72,7 @@
             )
         )
     }
-    val generateTestConfigurationTask = overrideProject.tasks.register(
+    val generateTestConfigurationTask = tasks.register(
         "${AndroidXImplPlugin.GENERATE_TEST_CONFIGURATION_TASK}$variantName",
         GenerateTestConfigurationTask::class.java
     ) { task ->
@@ -114,7 +118,7 @@
     afterEvaluate {
         val androidXExtension = extensions.getByType<AndroidXExtension>()
         generateTestConfigurationTask.configure {
-            it.enabled = hasAndroidTestSourceCode() && !androidXExtension.disableDeviceTests
+            it.enabled = androidXExtension.deviceTests.enabled && hasAndroidTestSourceCode()
         }
     }
     this.rootProject.tasks.findByName(ZIP_TEST_CONFIGS_WITH_APKS_TASK)!!
@@ -128,76 +132,143 @@
  * there is a test app in addition to the instrumentation app, and the only thing it configures is
  * the location of the testapp.
  */
-fun Project.addAppApkToTestConfigGeneration() {
-    if (isMacrobenchmarkTarget()) {
-        extensions.getByType<ApplicationAndroidComponentsExtension>().apply {
-            onVariants(selector().withBuildType("release")) { appVariant ->
-                getOrCreateMacrobenchmarkConfigTask().configure { configTask ->
-                    configTask.appFolder.set(appVariant.artifacts.get(SingleArtifact.APK))
-                    configTask.appLoader.set(appVariant.artifacts.getBuiltArtifactsLoader())
-                    configTask.outputAppApk.set(
-                        File(
-                            getTestConfigDirectory(),
-                            "${path.asFilenamePrefix()}-${appVariant.name}.apk"
-                        )
-                    )
-                    configTask.constrainedOutputAppApk.set(
-                        File(
-                            getConstrainedTestConfigDirectory(),
-                            "${path.asFilenamePrefix()}-${appVariant.name}.apk"
-                        )
-                    )
-                }
-                if (path == ":benchmark:integration-tests:macrobenchmark-target") {
-                    // Ugly workaround for b/188699825 where we hardcode that
-                    // :benchmark:integration-tests:macrobenchmark-target needs to be installed
-                    // for :benchmark:benchmark-macro tests to work.
-                    project(MACRO_PROJECT).tasks.withType(
-                        GenerateTestConfigurationTask::class.java
-                    ).named(
-                        "${AndroidXImplPlugin.GENERATE_TEST_CONFIGURATION_TASK}debugAndroidTest"
-                    ).configure { configTask ->
-                        configTask.appFolder.set(appVariant.artifacts.get(SingleArtifact.APK))
-                        configTask.appLoader.set(appVariant.artifacts.getBuiltArtifactsLoader())
-                        configTask.outputAppApk.set(
-                            File(
-                                getTestConfigDirectory(),
-                                "${MACRO_PROJECT.asFilenamePrefix()}-${appVariant.name}.apk"
-                            )
-                        )
-                        configTask.constrainedOutputAppApk.set(
-                            File(
-                                getConstrainedTestConfigDirectory(),
-                                "${MACRO_PROJECT.asFilenamePrefix()}-${appVariant.name}.apk"
-                            )
-                        )
-                    }
-                }
-            }
+fun Project.addAppApkToTestConfigGeneration(androidXExtension: AndroidXExtension) {
+
+    fun outputAppApkFile(
+        variant: Variant,
+        appProjectPath: String,
+        instrumentationProjectPath: String?
+    ): File {
+        var filename = appProjectPath.asFilenamePrefix()
+        if (instrumentationProjectPath != null) {
+            filename += "_for_${instrumentationProjectPath.asFilenamePrefix()}"
         }
-        return
+        filename += "-${variant.name}.apk"
+        return File(getTestConfigDirectory(), filename)
+    }
+    fun constrainedOutputAppApkFile(
+        variant: Variant,
+        path: String,
+        instrumentationPath: String?
+    ): File {
+        var filename = path.asFilenamePrefix()
+        if (instrumentationPath != null) {
+            filename += "-for-${instrumentationPath.asFilenamePrefix()}"
+        }
+        filename += "-${variant.name}.apk"
+        return File(getConstrainedTestConfigDirectory(), filename)
     }
 
-    extensions.getByType<ApplicationAndroidComponentsExtension>().apply {
-        onVariants(selector().withBuildType("debug")) { appVariant ->
+    // For application modules, the instrumentation apk is generated in the module itself
+    extensions.findByType(ApplicationAndroidComponentsExtension::class.java)?.apply {
+        onVariants(selector().withBuildType("debug")) { variant ->
             tasks.named(
-                AndroidXImplPlugin.GENERATE_TEST_CONFIGURATION_TASK +
-                    "${appVariant.name}AndroidTest"
-            ) { configTask ->
-                configTask as GenerateTestConfigurationTask
-                configTask.appFolder.set(appVariant.artifacts.get(SingleArtifact.APK))
-                configTask.appLoader.set(appVariant.artifacts.getBuiltArtifactsLoader())
-                configTask.outputAppApk.set(
-                    File(
-                        getTestConfigDirectory(),
-                        "${path.asFilenamePrefix()}-${appVariant.name}.apk"
-                    )
+                "${AndroidXImplPlugin.GENERATE_TEST_CONFIGURATION_TASK}${variant.name}AndroidTest",
+                GenerateTestConfigurationTask::class.java
+            ) { task ->
+                task.appFolder.set(variant.artifacts.get(SingleArtifact.APK))
+                task.appLoader.set(variant.artifacts.getBuiltArtifactsLoader())
+
+                // The target project is the same being evaluated
+                task.outputAppApk.set(outputAppApkFile(variant, path, null))
+                task.constrainedOutputAppApk.set(constrainedOutputAppApkFile(variant, path, null))
+            }
+        }
+    }
+
+    // For tests modules, the instrumentation apk is pulled from the <variant>TestedApks
+    // configuration. Note that also the associated test configuration task name is different
+    // from the application one.
+    extensions.findByType(TestAndroidComponentsExtension::class.java)?.apply {
+        onVariants(selector().all()) { variant ->
+            tasks.named(
+                "${AndroidXImplPlugin.GENERATE_TEST_CONFIGURATION_TASK}${variant.name}",
+                GenerateTestConfigurationTask::class.java
+            ) { task ->
+                task.appLoader.set(
+                    variant.artifacts.getBuiltArtifactsLoader()
                 )
-                configTask.constrainedOutputAppApk.set(
-                    File(
-                        getConstrainedTestConfigDirectory(),
-                        "${path.asFilenamePrefix()}-${appVariant.name}.apk"
-                    )
+
+                // The target app path is defined in the targetProjectPath field in the android
+                // extension of the test module
+                val targetProjectPath = project
+                    .extensions
+                    .getByType(TestExtension::class.java)
+                    .targetProjectPath
+                    ?: throw IllegalStateException("""
+                        Module `$path` does not have a targetProjectPath defined.
+                    """.trimIndent())
+                task.outputAppApk.set(
+                    outputAppApkFile(variant, targetProjectPath, path)
+                )
+                task.constrainedOutputAppApk.set(
+                    constrainedOutputAppApkFile(variant, targetProjectPath, path)
+                )
+
+                task.appFileCollection.from(
+                    configurations
+                        .named("${variant.name}TestedApks")
+                        .get()
+                        .incoming
+                        .artifactView {
+                            it.attributes { container ->
+                                container.attribute(
+                                    AndroidArtifacts.ARTIFACT_TYPE,
+                                    ArtifactType.APK.type
+                                )
+                            }
+                        }
+                        .files
+                )
+            }
+        }
+    }
+
+    // For library modules we only look at the build type debug. The target app project can be
+    // specified through the androidX extension, through: targetAppProjectForInstrumentationTest
+    // and targetAppProjectVariantForInstrumentationTest.
+    extensions.findByType(LibraryAndroidComponentsExtension::class.java)?.apply {
+        onVariants(selector().withBuildType("debug")) { variant ->
+
+            val targetAppProject =
+                androidXExtension.deviceTests.targetAppProject ?: return@onVariants
+            val targetAppProjectVariant =
+                androidXExtension.deviceTests.targetAppVariant
+
+            // Recreate the same configuration existing for test modules to pull the artifact
+            // from the application module specified in the deviceTests extension.
+            val configuration = configurations.create("${variant.name}TestedApks") { config ->
+                config.isCanBeResolved = true
+                config.isCanBeConsumed = false
+                config.attributes {
+                    it.attribute(VariantAttr.ATTRIBUTE, objects.named(targetAppProjectVariant))
+                    it.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
+                }
+                config
+                    .dependencies
+                    .add(project.dependencyFactory.create(targetAppProject))
+            }
+
+            tasks.named(
+                "${AndroidXImplPlugin.GENERATE_TEST_CONFIGURATION_TASK}${variant.name}AndroidTest",
+                GenerateTestConfigurationTask::class.java
+            ) { task ->
+                task.appLoader.set(variant.artifacts.getBuiltArtifactsLoader())
+
+                // The target app path is defined in the androidx extension
+                task.outputAppApk.set(
+                    outputAppApkFile(variant, targetAppProject.path, path)
+                )
+                task.constrainedOutputAppApk.set(
+                    constrainedOutputAppApkFile(variant, targetAppProject.path, path)
+                )
+
+                task.appFileCollection.from(
+                    configuration.incoming.artifactView { view ->
+                        view.attributes {
+                            it.attribute(AndroidArtifacts.ARTIFACT_TYPE, ArtifactType.APK.type)
+                        }
+                    }.files
                 )
             }
         }
@@ -329,94 +400,7 @@
     }
 }
 
-private fun Project.getOrCreateMacrobenchmarkConfigTask():
-    TaskProvider<GenerateTestConfigurationTask> {
-    val parentProject = this.parent!!
-    return try {
-        parentProject.tasks.withType(GenerateTestConfigurationTask::class.java)
-            .named(AndroidXImplPlugin.GENERATE_TEST_CONFIGURATION_TASK)
-    } catch (e: UnknownTaskException) {
-        parentProject.tasks.register(
-            AndroidXImplPlugin.GENERATE_TEST_CONFIGURATION_TASK,
-            GenerateTestConfigurationTask::class.java
-        )
-    }
-}
-
-private fun Project.configureMacrobenchmarkConfigTask(
-    variantName: String,
-    artifacts: Artifacts,
-    minSdk: Int,
-    testRunner: String
-) {
-    val configTask = getOrCreateMacrobenchmarkConfigTask()
-    configTask.configure { task ->
-        val androidXExtension = extensions.getByType<AndroidXExtension>()
-        val fileNamePrefix = path.asFilenamePrefix()
-        task.testFolder.set(artifacts.get(SingleArtifact.APK))
-        task.testLoader.set(artifacts.getBuiltArtifactsLoader())
-        task.outputTestApk.set(
-            File(getTestConfigDirectory(), "${path.asFilenamePrefix()}-$variantName.apk")
-        )
-        task.constrainedOutputTestApk.set(
-            File(
-                getConstrainedTestConfigDirectory(),
-                "${path.asFilenamePrefix()}-$variantName.apk"
-            )
-        )
-        task.additionalApkKeys.set(androidXExtension.additionalDeviceTestApkKeys)
-        task.additionalTags.set(androidXExtension.additionalDeviceTestTags)
-        task.outputXml.fileValue(
-            File(getTestConfigDirectory(), "$fileNamePrefix$variantName.xml")
-        )
-        task.outputJson.fileValue(
-            File(getTestConfigDirectory(), "_$fileNamePrefix$variantName.json")
-        )
-        task.constrainedOutputXml.fileValue(
-            File(
-                getTestConfigDirectory(),
-                "${path.asFilenamePrefix()}$variantName.xml"
-            )
-        )
-        task.minSdk.set(minSdk)
-        task.hasBenchmarkPlugin.set(hasBenchmarkPlugin())
-        task.testRunner.set(testRunner)
-        task.testProjectPath.set(path)
-        task.presubmit.set(isPresubmitBuild())
-        val detector = AffectedModuleDetector.getInstance(project)
-        task.affectedModuleDetectorSubset.set(
-            project.provider {
-                detector.getSubset(task)
-            }
-        )
-
-        AffectedModuleDetector.configureTaskGuard(task)
-    }
-    // Disable xml generation for projects that have no test sources
-    afterEvaluate {
-        val androidXExtension = extensions.getByType<AndroidXExtension>()
-        configTask.configure {
-            it.enabled = hasAndroidTestSourceCode() && !androidXExtension.disableDeviceTests
-        }
-    }
-    rootProject.tasks.findByName(ZIP_TEST_CONFIGS_WITH_APKS_TASK)!!
-        .dependsOn(configTask)
-    rootProject.tasks.findByName(ZIP_CONSTRAINED_TEST_CONFIGS_WITH_APKS_TASK)!!
-        .dependsOn(configTask)
-}
-
-/**
- * Tells whether this project is the macrobenchmark-target project
- */
-fun Project.isMacrobenchmarkTarget(): Boolean {
-    return path.endsWith("macrobenchmark-target")
-}
-
 fun Project.configureTestConfigGeneration(baseExtension: BaseExtension) {
-    if (isMacrobenchmarkTarget()) {
-        // macrobenchmark target projects use special setup. See addAppApkToTestConfigGeneration
-        return
-    }
     extensions.getByType(AndroidComponentsExtension::class.java).apply {
         onVariants { variant ->
             var name: String? = null
@@ -453,14 +437,6 @@
                         isMedia2 = false
                     )
                 }
-                path.endsWith("macrobenchmark") -> {
-                    configureMacrobenchmarkConfigTask(
-                        name,
-                        artifacts,
-                        baseExtension.defaultConfig.minSdk!!,
-                        baseExtension.defaultConfig.testInstrumentationRunner!!
-                    )
-                }
                 else -> {
                     createTestConfigurationGenerationTask(
                         name,
@@ -473,5 +449,3 @@
         }
     }
 }
-
-private const val MACRO_PROJECT = ":benchmark:benchmark-macro"
\ No newline at end of file
diff --git a/compose/integration-tests/macrobenchmark/build.gradle b/compose/integration-tests/macrobenchmark/build.gradle
index 5cb3474..7739110 100644
--- a/compose/integration-tests/macrobenchmark/build.gradle
+++ b/compose/integration-tests/macrobenchmark/build.gradle
@@ -14,11 +14,9 @@
  * limitations under the License.
  */
 
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
 plugins {
     id("AndroidXPlugin")
-    id("com.android.library")
+    id("com.android.test")
     id("kotlin-android")
 }
 
@@ -27,25 +25,22 @@
         minSdkVersion 23
     }
     namespace "androidx.compose.integration.macrobenchmark"
+    targetProjectPath = ":compose:integration-tests:macrobenchmark-target"
+    experimentalProperties["android.experimental.self-instrumenting"] = true
 }
 
+// Create a release build type and make sure it's the only one enabled.
+// This is needed because we benchmark the release build type only.
+android.buildTypes { release {} }
+androidComponents { beforeVariants(selector().all()) { enabled = buildType == 'release' } }
+
 dependencies {
-    androidTestImplementation(project(":benchmark:benchmark-junit4"))
-    androidTestImplementation(project(":benchmark:benchmark-macro-junit4"))
-    androidTestImplementation(project(":internal-testutils-macrobenchmark"))
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testUiautomator)
-}
-
-// Define a task dependency so the app is installed before we run macro benchmarks.
-afterEvaluate {
-    tasks.getByPath(":compose:integration-tests:macrobenchmark:connectedDebugAndroidTest")
-            .dependsOn(
-                    tasks.getByPath(
-                            ":compose:integration-tests:macrobenchmark-target:installRelease"
-                    )
-            )
+    implementation(project(":benchmark:benchmark-junit4"))
+    implementation(project(":benchmark:benchmark-macro-junit4"))
+    implementation(project(":internal-testutils-macrobenchmark"))
+    implementation(libs.testRules)
+    implementation(libs.testExtJunit)
+    implementation(libs.testCore)
+    implementation(libs.testRunner)
+    implementation(libs.testUiautomator)
 }
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/AndroidViewListScrollBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/AndroidViewListScrollBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/AndroidViewListScrollBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/AndroidViewListScrollBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/AndroidViewPagerBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/AndroidViewPagerBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/AndroidViewPagerBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/AndroidViewPagerBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/DifferentTypesListScrollBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/DifferentTypesListScrollBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/DifferentTypesListScrollBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/DifferentTypesListScrollBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/FullyDrawnStartupBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/FullyDrawnStartupBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/FullyDrawnStartupBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/FullyDrawnStartupBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/GridBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/GridBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/GridBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/GridBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/IoSettingsStartupBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/IoSettingsStartupBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/IoSettingsStartupBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/IoSettingsStartupBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/LazyBoxWithConstraintsScrollBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/LazyBoxWithConstraintsScrollBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/LazyBoxWithConstraintsScrollBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/LazyBoxWithConstraintsScrollBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/NestedListsScrollBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/NestedListsScrollBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/NestedListsScrollBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/NestedListsScrollBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/PagerAsCarouselBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/PagerAsCarouselBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/PagerAsCarouselBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/PagerAsCarouselBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/PagerBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/PagerBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/PagerBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/PagerBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/RecyclerViewAsCarouselBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/RecyclerViewAsCarouselBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/RecyclerViewAsCarouselBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/RecyclerViewAsCarouselBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/RecyclerViewListScrollBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/RecyclerViewListScrollBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/RecyclerViewListScrollBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/RecyclerViewListScrollBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/SmallListBaselineProfile.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/SmallListBaselineProfile.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/SmallListBaselineProfile.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/SmallListBaselineProfile.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/SmallListStartupBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/SmallListStartupBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/SmallListStartupBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/SmallListStartupBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/TrivialListScrollBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialListScrollBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/TrivialListScrollBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialListScrollBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/TrivialStartupBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/TrivialStartupBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupBenchmark.kt
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt
similarity index 100%
rename from compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index 1f5b379..b6e6d45 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -758,16 +758,20 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TabPosition {
+    method public float getContentWidth();
     method public float getLeft();
     method public float getRight();
     method public float getWidth();
+    property public final float contentWidth;
     property public final float left;
     property public final float right;
     property public final float width;
   }
 
   public final class TabRowDefaults {
-    method @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method @Deprecated @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method @androidx.compose.runtime.Composable public void PrimaryIndicator(optional androidx.compose.ui.Modifier modifier, optional float width, optional float height, optional long color, optional androidx.compose.ui.graphics.Shape shape);
+    method @androidx.compose.runtime.Composable public void SecondaryIndicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method @androidx.compose.runtime.Composable public long getContentColor();
     method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material3.TabPosition currentTabPosition);
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index b2471cb..a2231ee 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -1145,16 +1145,20 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TabPosition {
+    method public float getContentWidth();
     method public float getLeft();
     method public float getRight();
     method public float getWidth();
+    property public final float contentWidth;
     property public final float left;
     property public final float right;
     property public final float width;
   }
 
   public final class TabRowDefaults {
-    method @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method @Deprecated @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method @androidx.compose.runtime.Composable public void PrimaryIndicator(optional androidx.compose.ui.Modifier modifier, optional float width, optional float height, optional long color, optional androidx.compose.ui.graphics.Shape shape);
+    method @androidx.compose.runtime.Composable public void SecondaryIndicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method @androidx.compose.runtime.Composable public long getContentColor();
     method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material3.TabPosition currentTabPosition);
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 1f5b379..b6e6d45 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -758,16 +758,20 @@
   }
 
   @androidx.compose.runtime.Immutable public final class TabPosition {
+    method public float getContentWidth();
     method public float getLeft();
     method public float getRight();
     method public float getWidth();
+    property public final float contentWidth;
     property public final float left;
     property public final float right;
     property public final float width;
   }
 
   public final class TabRowDefaults {
-    method @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method @Deprecated @androidx.compose.runtime.Composable public void Indicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
+    method @androidx.compose.runtime.Composable public void PrimaryIndicator(optional androidx.compose.ui.Modifier modifier, optional float width, optional float height, optional long color, optional androidx.compose.ui.graphics.Shape shape);
+    method @androidx.compose.runtime.Composable public void SecondaryIndicator(optional androidx.compose.ui.Modifier modifier, optional float height, optional long color);
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method @androidx.compose.runtime.Composable public long getContentColor();
     method public androidx.compose.ui.Modifier tabIndicatorOffset(androidx.compose.ui.Modifier, androidx.compose.material3.TabPosition currentTabPosition);
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
index 1d6480b..aef2f9c 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
@@ -101,6 +101,7 @@
 import androidx.compose.material3.samples.PinnedTopAppBar
 import androidx.compose.material3.samples.PlainTooltipSample
 import androidx.compose.material3.samples.PlainTooltipWithManualInvocationSample
+import androidx.compose.material3.samples.PrimaryTabs
 import androidx.compose.material3.samples.RadioButtonSample
 import androidx.compose.material3.samples.RadioGroupSample
 import androidx.compose.material3.samples.RangeSliderSample
@@ -113,8 +114,11 @@
 import androidx.compose.material3.samples.ScaffoldWithMultilineSnackbar
 import androidx.compose.material3.samples.ScaffoldWithSimpleSnackbar
 import androidx.compose.material3.samples.ScrollingFancyIndicatorContainerTabs
+import androidx.compose.material3.samples.ScrollingPrimaryTabs
+import androidx.compose.material3.samples.ScrollingSecondaryTabs
 import androidx.compose.material3.samples.ScrollingTextTabs
 import androidx.compose.material3.samples.SearchBarSample
+import androidx.compose.material3.samples.SecondaryTabs
 import androidx.compose.material3.samples.SimpleBottomAppBar
 import androidx.compose.material3.samples.SimpleBottomSheetScaffoldSample
 import androidx.compose.material3.samples.SimpleCenterAlignedTopAppBar
@@ -894,6 +898,20 @@
 private const val TabsExampleSourceUrl = "$SampleSourceUrl/TabSamples.kt"
 val TabsExamples = listOf(
     Example(
+        name = ::PrimaryTabs.name,
+        description = TabsExampleDescription,
+        sourceUrl = TabsExampleSourceUrl
+    ) {
+        PrimaryTabs()
+    },
+    Example(
+        name = ::SecondaryTabs.name,
+        description = TabsExampleDescription,
+        sourceUrl = TabsExampleSourceUrl
+    ) {
+        SecondaryTabs()
+    },
+    Example(
         name = ::TextTabs.name,
         description = TabsExampleDescription,
         sourceUrl = TabsExampleSourceUrl
@@ -922,6 +940,20 @@
         LeadingIconTabs()
     },
     Example(
+        name = ::ScrollingPrimaryTabs.name,
+        description = TabsExampleDescription,
+        sourceUrl = TabsExampleSourceUrl
+    ) {
+        ScrollingPrimaryTabs()
+    },
+    Example(
+        name = ::ScrollingSecondaryTabs.name,
+        description = TabsExampleDescription,
+        sourceUrl = TabsExampleSourceUrl
+    ) {
+        ScrollingSecondaryTabs()
+    },
+    Example(
         name = ::ScrollingTextTabs.name,
         description = TabsExampleDescription,
         sourceUrl = TabsExampleSourceUrl
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TabSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TabSamples.kt
index bbeb8b3..c19a692 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TabSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TabSamples.kt
@@ -19,6 +19,7 @@
 import androidx.annotation.Sampled
 import androidx.compose.animation.animateColor
 import androidx.compose.animation.core.animateDp
+import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.animation.core.spring
 import androidx.compose.animation.core.updateTransition
 import androidx.compose.foundation.BorderStroke
@@ -29,24 +30,25 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.material3.Icon
+import androidx.compose.material3.LeadingIconTab
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.ScrollableTabRow
 import androidx.compose.material3.Tab
-import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
 import androidx.compose.material3.TabPosition
 import androidx.compose.material3.TabRow
-import androidx.compose.material3.LeadingIconTab
+import androidx.compose.material3.TabRowDefaults
+import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
 import androidx.compose.material3.Text
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -59,6 +61,58 @@
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 
+@Composable
+fun PrimaryTabs() {
+    var state by remember { mutableStateOf(0) }
+    val titles = listOf("Tab 1", "Tab 2", "Tab 3 with lots of text")
+    Column {
+        TabRow(selectedTabIndex = state, indicator = @Composable { tabPositions ->
+            if (state < tabPositions.size) {
+                val width by animateDpAsState(targetValue = tabPositions[state].contentWidth)
+                TabRowDefaults.PrimaryIndicator(
+                    modifier = Modifier.tabIndicatorOffset(tabPositions[state]),
+                    width = width
+                )
+            }
+        }) {
+            titles.forEachIndexed { index, title ->
+                Tab(
+                    selected = state == index,
+                    onClick = { state = index },
+                    text = { Text(text = title, maxLines = 2, overflow = TextOverflow.Ellipsis) }
+                )
+            }
+        }
+        Text(
+            modifier = Modifier.align(Alignment.CenterHorizontally),
+            text = "Primary tab ${state + 1} selected",
+            style = MaterialTheme.typography.bodyLarge
+        )
+    }
+}
+
+@Composable
+fun SecondaryTabs() {
+    var state by remember { mutableStateOf(0) }
+    val titles = listOf("Tab 1", "Tab 2", "Tab 3 with lots of text")
+    Column {
+        TabRow(selectedTabIndex = state) {
+            titles.forEachIndexed { index, title ->
+                Tab(
+                    selected = state == index,
+                    onClick = { state = index },
+                    text = { Text(text = title, maxLines = 2, overflow = TextOverflow.Ellipsis) }
+                )
+            }
+        }
+        Text(
+            modifier = Modifier.align(Alignment.CenterHorizontally),
+            text = "Secondary tab ${state + 1} selected",
+            style = MaterialTheme.typography.bodyLarge
+        )
+    }
+}
+
 @Preview
 @Sampled
 @Composable
@@ -160,6 +214,80 @@
 }
 
 @Composable
+fun ScrollingPrimaryTabs() {
+    var state by remember { mutableStateOf(0) }
+    val titles = listOf(
+        "Tab 1",
+        "Tab 2",
+        "Tab 3 with lots of text",
+        "Tab 4",
+        "Tab 5",
+        "Tab 6 with lots of text",
+        "Tab 7",
+        "Tab 8",
+        "Tab 9 with lots of text",
+        "Tab 10"
+    )
+    Column {
+        ScrollableTabRow(selectedTabIndex = state, indicator = @Composable { tabPositions ->
+            if (state < tabPositions.size) {
+                val width by animateDpAsState(targetValue = tabPositions[state].contentWidth)
+                TabRowDefaults.PrimaryIndicator(
+                    modifier = Modifier.tabIndicatorOffset(tabPositions[state]),
+                    width = width
+                )
+            }
+        }) {
+            titles.forEachIndexed { index, title ->
+                Tab(
+                    selected = state == index,
+                    onClick = { state = index },
+                    text = { Text(title) }
+                )
+            }
+        }
+        Text(
+            modifier = Modifier.align(Alignment.CenterHorizontally),
+            text = "Scrolling primary tab ${state + 1} selected",
+            style = MaterialTheme.typography.bodyLarge
+        )
+    }
+}
+
+@Composable
+fun ScrollingSecondaryTabs() {
+    var state by remember { mutableStateOf(0) }
+    val titles = listOf(
+        "Tab 1",
+        "Tab 2",
+        "Tab 3 with lots of text",
+        "Tab 4",
+        "Tab 5",
+        "Tab 6 with lots of text",
+        "Tab 7",
+        "Tab 8",
+        "Tab 9 with lots of text",
+        "Tab 10"
+    )
+    Column {
+        ScrollableTabRow(selectedTabIndex = state) {
+            titles.forEachIndexed { index, title ->
+                Tab(
+                    selected = state == index,
+                    onClick = { state = index },
+                    text = { Text(title) }
+                )
+            }
+        }
+        Text(
+            modifier = Modifier.align(Alignment.CenterHorizontally),
+            text = "Scrolling secondary tab ${state + 1} selected",
+            style = MaterialTheme.typography.bodyLarge
+        )
+    }
+}
+
+@Composable
 fun ScrollingTextTabs() {
     var state by remember { mutableStateOf(0) }
     val titles = listOf(
@@ -201,7 +329,11 @@
     Column {
         TabRow(selectedTabIndex = state) {
             titles.forEachIndexed { index, title ->
-                FancyTab(title = title, onClick = { state = index }, selected = (index == state))
+                FancyTab(
+                    title = title,
+                    onClick = { state = index },
+                    selected = (index == state)
+                )
             }
         }
         Text(
@@ -334,7 +466,8 @@
                     .align(Alignment.CenterHorizontally)
                     .background(
                         color = if (selected) MaterialTheme.colorScheme.primary
-                        else MaterialTheme.colorScheme.background)
+                        else MaterialTheme.colorScheme.background
+                    )
             )
             Text(
                 text = title,
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabScreenshotTest.kt
index cced64f..318b5c0 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabScreenshotTest.kt
@@ -59,7 +59,7 @@
     val screenshotRule = AndroidXScreenshotTestRule(GOLDEN_MATERIAL3)
 
     @Test
-    fun lightTheme() {
+    fun lightTheme_primary() {
         val interactionSource = MutableInteractionSource()
 
         var scope: CoroutineScope? = null
@@ -67,7 +67,7 @@
         composeTestRule.setContent {
             scope = rememberCoroutineScope()
             MaterialTheme(lightColorScheme()) {
-                DefaultTabs(interactionSource)
+                DefaultPrimaryTabs(interactionSource)
             }
         }
 
@@ -75,12 +75,12 @@
             scope = scope!!,
             interactionSource = interactionSource,
             interaction = null,
-            goldenIdentifier = "tabs_lightTheme"
+            goldenIdentifier = "tabs_lightTheme_primary"
         )
     }
 
     @Test
-    fun lightTheme_pressed() {
+    fun lightTheme_secondary() {
         val interactionSource = MutableInteractionSource()
 
         var scope: CoroutineScope? = null
@@ -88,7 +88,28 @@
         composeTestRule.setContent {
             scope = rememberCoroutineScope()
             MaterialTheme(lightColorScheme()) {
-                DefaultTabs(interactionSource)
+                DefaultSecondaryTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "tabs_lightTheme_secondary"
+        )
+    }
+
+    @Test
+    fun lightTheme_primary_pressed() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(lightColorScheme()) {
+                DefaultPrimaryTabs(interactionSource)
             }
         }
 
@@ -96,54 +117,12 @@
             scope = scope!!,
             interactionSource = interactionSource,
             interaction = PressInteraction.Press(Offset(10f, 10f)),
-            goldenIdentifier = "tabs_lightTheme_pressed"
+            goldenIdentifier = "tabs_lightTheme_primary_pressed"
         )
     }
 
     @Test
-    fun darkTheme() {
-        val interactionSource = MutableInteractionSource()
-
-        var scope: CoroutineScope? = null
-
-        composeTestRule.setContent {
-            scope = rememberCoroutineScope()
-            MaterialTheme(darkColorScheme()) {
-                DefaultTabs(interactionSource)
-            }
-        }
-
-        assertTabsMatch(
-            scope = scope!!,
-            interactionSource = interactionSource,
-            interaction = null,
-            goldenIdentifier = "tabs_darkTheme"
-        )
-    }
-
-    @Test
-    fun darkTheme_pressed() {
-        val interactionSource = MutableInteractionSource()
-
-        var scope: CoroutineScope? = null
-
-        composeTestRule.setContent {
-            scope = rememberCoroutineScope()
-            MaterialTheme(darkColorScheme()) {
-                DefaultTabs(interactionSource)
-            }
-        }
-
-        assertTabsMatch(
-            scope = scope!!,
-            interactionSource = interactionSource,
-            interaction = PressInteraction.Press(Offset(10f, 10f)),
-            goldenIdentifier = "tabs_darkTheme_pressed"
-        )
-    }
-
-    @Test
-    fun leadingIconTabs_lightTheme() {
+    fun lightTheme_secondary_pressed() {
         val interactionSource = MutableInteractionSource()
 
         var scope: CoroutineScope? = null
@@ -151,20 +130,20 @@
         composeTestRule.setContent {
             scope = rememberCoroutineScope()
             MaterialTheme(lightColorScheme()) {
-                DefaultLeadingIconTabs(interactionSource)
+                DefaultSecondaryTabs(interactionSource)
             }
         }
 
         assertTabsMatch(
             scope = scope!!,
             interactionSource = interactionSource,
-            interaction = null,
-            goldenIdentifier = "leadingIconTabs_lightTheme"
+            interaction = PressInteraction.Press(Offset(10f, 10f)),
+            goldenIdentifier = "tabs_lightTheme_secondary_pressed"
         )
     }
 
     @Test
-    fun leadingIconTabs_darkTheme() {
+    fun darkTheme_primary() {
         val interactionSource = MutableInteractionSource()
 
         var scope: CoroutineScope? = null
@@ -172,7 +151,7 @@
         composeTestRule.setContent {
             scope = rememberCoroutineScope()
             MaterialTheme(darkColorScheme()) {
-                DefaultLeadingIconTabs(interactionSource)
+                DefaultPrimaryTabs(interactionSource)
             }
         }
 
@@ -180,7 +159,342 @@
             scope = scope!!,
             interactionSource = interactionSource,
             interaction = null,
-            goldenIdentifier = "leadingIconTabs_darkTheme"
+            goldenIdentifier = "tabs_darkTheme_primary"
+        )
+    }
+
+    @Test
+    fun darkTheme_secondary() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(darkColorScheme()) {
+                DefaultSecondaryTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "tabs_darkTheme_secondary"
+        )
+    }
+
+    @Test
+    fun darkTheme_primary_pressed() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(darkColorScheme()) {
+                DefaultPrimaryTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = PressInteraction.Press(Offset(10f, 10f)),
+            goldenIdentifier = "tabs_darkTheme_primary_pressed"
+        )
+    }
+
+    @Test
+    fun darkTheme_secondary_pressed() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(darkColorScheme()) {
+                DefaultSecondaryTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = PressInteraction.Press(Offset(10f, 10f)),
+            goldenIdentifier = "tabs_darkTheme_secondary_pressed"
+        )
+    }
+
+    @Test
+    fun customTabs_lightTheme_primary() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(lightColorScheme()) {
+                CustomPrimaryTabs(
+                    interactionSource,
+                    containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+                    selectedContentColor = MaterialTheme.colorScheme.onTertiary,
+                    unselectedContentColor = Color.Black
+                )
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "customTabs_lightTheme_primary"
+        )
+    }
+
+    @Test
+    fun customTabs_lightTheme_secondary() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(lightColorScheme()) {
+                CustomSecondaryTabs(
+                    interactionSource,
+                    containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+                    selectedContentColor = MaterialTheme.colorScheme.onTertiary,
+                    unselectedContentColor = Color.Black
+                )
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "customTabs_lightTheme_secondary"
+        )
+    }
+
+    @Test
+    fun customTabs_darkTheme_primary() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(darkColorScheme()) {
+                CustomPrimaryTabs(
+                    interactionSource,
+                    containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+                    selectedContentColor = MaterialTheme.colorScheme.onTertiary,
+                    unselectedContentColor = Color.Black
+                )
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "customTabs_darkTheme_primary"
+        )
+    }
+
+    @Test
+    fun customTabs_darkTheme_secondary() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(darkColorScheme()) {
+                CustomSecondaryTabs(
+                    interactionSource,
+                    containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+                    selectedContentColor = MaterialTheme.colorScheme.onTertiary,
+                    unselectedContentColor = Color.Black
+                )
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "customTabs_darkTheme_secondary"
+        )
+    }
+
+    @Test
+    fun leadingIconTabs_lightTheme_primary() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(lightColorScheme()) {
+                DefaultPrimaryLeadingIconTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "leadingIconTabs_lightTheme_primary"
+        )
+    }
+
+    @Test
+    fun leadingIconTabs_lightTheme_secondary() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(lightColorScheme()) {
+                DefaultSecondaryLeadingIconTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "leadingIconTabs_lightTheme_secondary"
+        )
+    }
+
+    @Test
+    fun leadingIconTabs_darkTheme_primary() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(darkColorScheme()) {
+                DefaultPrimaryLeadingIconTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "leadingIconTabs_darkTheme_primary"
+        )
+    }
+
+    @Test
+    fun leadingIconTabs_darkTheme_secondary() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(darkColorScheme()) {
+                DefaultSecondaryLeadingIconTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "leadingIconTabs_darkTheme_secondary"
+        )
+    }
+
+    @Test
+    fun lightTheme_primary_scrollable() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(lightColorScheme()) {
+                DefaultPrimaryScrollableTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "tabs_lightTheme_primary_scrollable"
+        )
+    }
+
+    @Test
+    fun lightTheme_secondary_scrollable() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(lightColorScheme()) {
+                DefaultSecondaryScrollableTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "tabs_lightTheme_secondary_scrollable"
+        )
+    }
+
+    @Test
+    fun darkTheme_primary_scrollable() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(darkColorScheme()) {
+                DefaultPrimaryScrollableTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "tabs_darkTheme_primary_scrollable"
+        )
+    }
+
+    @Test
+    fun darkTheme_secondary_scrollable() {
+        val interactionSource = MutableInteractionSource()
+
+        var scope: CoroutineScope? = null
+
+        composeTestRule.setContent {
+            scope = rememberCoroutineScope()
+            MaterialTheme(darkColorScheme()) {
+                DefaultSecondaryScrollableTabs(interactionSource)
+            }
+        }
+
+        assertTabsMatch(
+            scope = scope!!,
+            interactionSource = interactionSource,
+            interaction = null,
+            goldenIdentifier = "tabs_darkTheme_secondary_scrollable"
         )
     }
 
@@ -213,23 +527,66 @@
         }
 
         // Capture and compare screenshots
-        composeTestRule.onNodeWithTag(Tag)
+        composeTestRule.onNodeWithTag(TAG)
             .captureToImage()
             .assertAgainstGolden(screenshotRule, goldenIdentifier)
     }
 }
 
 /**
- * Default colored [TabRow] with three [Tab]s. The first [Tab] is selected, and the rest are not.
+ * Default primary colored [TabRow] with three [Tab]s. The first [Tab] is selected, and the rest are not.
  *
  * @param interactionSource the [MutableInteractionSource] for the first [Tab], to control its
  * visual state.
  */
 @Composable
-private fun DefaultTabs(
+private fun DefaultPrimaryTabs(
     interactionSource: MutableInteractionSource
 ) {
-    Box(Modifier.semantics(mergeDescendants = true) {}.testTag(Tag)) {
+    Box(
+        Modifier
+            .semantics(mergeDescendants = true) {}
+            .testTag(TAG)) {
+        TabRow(selectedTabIndex = 0, indicator = @Composable { tabPositions ->
+            TabRowDefaults.PrimaryIndicator(
+                modifier = Modifier.tabIndicatorOffset(tabPositions[0]),
+                width = tabPositions[0].contentWidth
+            )
+        }) {
+            Tab(
+                selected = true,
+                onClick = {},
+                text = { Text("TAB") },
+                interactionSource = interactionSource
+            )
+            Tab(
+                selected = false,
+                onClick = {},
+                text = { Text("TAB") }
+            )
+            Tab(
+                selected = false,
+                onClick = {},
+                text = { Text("TAB") }
+            )
+        }
+    }
+}
+
+/**
+ * Default secondary colored [TabRow] with three [Tab]s. The first [Tab] is selected, and the rest are not.
+ *
+ * @param interactionSource the [MutableInteractionSource] for the first [Tab], to control its
+ * visual state.
+ */
+@Composable
+private fun DefaultSecondaryTabs(
+    interactionSource: MutableInteractionSource
+) {
+    Box(
+        Modifier
+            .semantics(mergeDescendants = true) {}
+            .testTag(TAG)) {
         TabRow(selectedTabIndex = 0) {
             Tab(
                 selected = true,
@@ -252,7 +609,7 @@
 }
 
 /**
- * Custom colored [TabRow] with three [Tab]s. The first [Tab] is selected, and the rest are not.
+ * Custom primary colored [TabRow] with three [Tab]s. The first [Tab] is selected, and the rest are not.
  *
  * @param interactionSource the [MutableInteractionSource] for the first [Tab], to control its
  * visual state.
@@ -261,17 +618,75 @@
  * @param unselectedContentColor the content color for an unselected [Tab] (second and third tabs)
  */
 @Composable
-private fun CustomTabs(
+private fun CustomPrimaryTabs(
     interactionSource: MutableInteractionSource,
     containerColor: Color,
     selectedContentColor: Color,
     unselectedContentColor: Color
 ) {
-    Box(Modifier.semantics(mergeDescendants = true) {}.testTag(Tag)) {
+    Box(
+        Modifier
+            .semantics(mergeDescendants = true) {}
+            .testTag(TAG)) {
         TabRow(selectedTabIndex = 0,
             containerColor = containerColor,
             indicator = @Composable { tabPositions ->
-                TabRowDefaults.Indicator(
+                TabRowDefaults.PrimaryIndicator(
+                    modifier = Modifier.tabIndicatorOffset(tabPositions[0]),
+                    width = tabPositions[0].contentWidth,
+                    color = selectedContentColor
+                )
+            }) {
+            Tab(
+                selected = true,
+                onClick = {},
+                text = { Text("TAB") },
+                selectedContentColor = selectedContentColor,
+                unselectedContentColor = unselectedContentColor,
+                interactionSource = interactionSource
+            )
+            Tab(
+                selected = false,
+                onClick = {},
+                text = { Text("TAB") },
+                selectedContentColor = selectedContentColor,
+                unselectedContentColor = unselectedContentColor
+            )
+            Tab(
+                selected = false,
+                onClick = {},
+                text = { Text("TAB") },
+                selectedContentColor = selectedContentColor,
+                unselectedContentColor = unselectedContentColor
+            )
+        }
+    }
+}
+
+/**
+ * Custom secondary colored [TabRow] with three [Tab]s. The first [Tab] is selected, and the rest are not.
+ *
+ * @param interactionSource the [MutableInteractionSource] for the first [Tab], to control its
+ * visual state.
+ * @param containerColor the containerColor of the [TabRow]
+ * @param selectedContentColor the content color for a selected [Tab] (first tab)
+ * @param unselectedContentColor the content color for an unselected [Tab] (second and third tabs)
+ */
+@Composable
+private fun CustomSecondaryTabs(
+    interactionSource: MutableInteractionSource,
+    containerColor: Color,
+    selectedContentColor: Color,
+    unselectedContentColor: Color
+) {
+    Box(
+        Modifier
+            .semantics(mergeDescendants = true) {}
+            .testTag(TAG)) {
+        TabRow(selectedTabIndex = 0,
+            containerColor = containerColor,
+            indicator = @Composable { tabPositions ->
+                TabRowDefaults.SecondaryIndicator(
                     modifier = Modifier.tabIndicatorOffset(tabPositions[0]),
                     color = selectedContentColor
                 )
@@ -303,17 +718,64 @@
 }
 
 /**
- * Default colored [TabRow] with three [LeadingIconTab]s. The first [LeadingIconTab] is selected,
+ * Default primary colored [TabRow] with three [LeadingIconTab]s. The first [LeadingIconTab] is selected,
  * and the rest are not.
  *
  * @param interactionSource the [MutableInteractionSource] for the first [LeadingIconTab], to control its
  * visual state.
  */
 @Composable
-private fun DefaultLeadingIconTabs(
+private fun DefaultPrimaryLeadingIconTabs(
     interactionSource: MutableInteractionSource
 ) {
-    Box(Modifier.semantics(mergeDescendants = true) {}.testTag(Tag)) {
+    Box(
+        Modifier
+            .semantics(mergeDescendants = true) {}
+            .testTag(TAG)) {
+        TabRow(selectedTabIndex = 0, indicator = @Composable { tabPositions ->
+            TabRowDefaults.PrimaryIndicator(
+                modifier = Modifier.tabIndicatorOffset(tabPositions[0]),
+                width = tabPositions[0].contentWidth
+            )
+        }) {
+            LeadingIconTab(
+                selected = true,
+                onClick = {},
+                text = { Text("TAB") },
+                icon = { Icon(Icons.Filled.Favorite, contentDescription = "Favorite") },
+                interactionSource = interactionSource
+            )
+            LeadingIconTab(
+                selected = false,
+                onClick = {},
+                text = { Text("TAB") },
+                icon = { Icon(Icons.Filled.Favorite, contentDescription = "Favorite") }
+            )
+            LeadingIconTab(
+                selected = false,
+                onClick = {},
+                text = { Text("TAB") },
+                icon = { Icon(Icons.Filled.Favorite, contentDescription = "Favorite") }
+            )
+        }
+    }
+}
+
+/**
+ * Default secondary colored [TabRow] with three [LeadingIconTab]s. The first [LeadingIconTab] is selected,
+ * and the rest are not.
+ *
+ * @param interactionSource the [MutableInteractionSource] for the first [LeadingIconTab], to control its
+ * visual state.
+ */
+@Composable
+private fun DefaultSecondaryLeadingIconTabs(
+    interactionSource: MutableInteractionSource
+) {
+    Box(
+        Modifier
+            .semantics(mergeDescendants = true) {}
+            .testTag(TAG)) {
         TabRow(selectedTabIndex = 0) {
             LeadingIconTab(
                 selected = true,
@@ -338,4 +800,79 @@
     }
 }
 
-private const val Tag = "Tab"
+/**
+ * Default primary colored [ScrollableTabRow] with three [Tab]s. The first [Tab] is selected, and the rest are not.
+ *
+ * @param interactionSource the [MutableInteractionSource] for the first [Tab], to control its
+ * visual state.
+ */
+@Composable
+private fun DefaultPrimaryScrollableTabs(
+    interactionSource: MutableInteractionSource
+) {
+    Box(
+        Modifier
+            .semantics(mergeDescendants = true) {}
+            .testTag(TAG)) {
+        ScrollableTabRow(selectedTabIndex = 0, indicator = @Composable { tabPositions ->
+            TabRowDefaults.PrimaryIndicator(
+                modifier = Modifier.tabIndicatorOffset(tabPositions[0]),
+                width = tabPositions[0].contentWidth
+            )
+        }) {
+            Tab(
+                selected = true,
+                onClick = {},
+                text = { Text("TAB") },
+                interactionSource = interactionSource
+            )
+            Tab(
+                selected = false,
+                onClick = {},
+                text = { Text("TAB") }
+            )
+            Tab(
+                selected = false,
+                onClick = {},
+                text = { Text("TAB") }
+            )
+        }
+    }
+}
+
+/**
+ * Default secondary colored [ScrollableTabRow] with three [Tab]s. The first [Tab] is selected, and the rest are not.
+ *
+ * @param interactionSource the [MutableInteractionSource] for the first [Tab], to control its
+ * visual state.
+ */
+@Composable
+private fun DefaultSecondaryScrollableTabs(
+    interactionSource: MutableInteractionSource
+) {
+    Box(
+        Modifier
+            .semantics(mergeDescendants = true) {}
+            .testTag(TAG)) {
+        ScrollableTabRow(selectedTabIndex = 0) {
+            Tab(
+                selected = true,
+                onClick = {},
+                text = { Text("TAB") },
+                interactionSource = interactionSource
+            )
+            Tab(
+                selected = false,
+                onClick = {},
+                text = { Text("TAB") }
+            )
+            Tab(
+                selected = false,
+                onClick = {},
+                text = { Text("TAB") }
+            )
+        }
+    }
+}
+
+private const val TAG = "Tab"
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt
index 4ff880a..7b9ed6e 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/TabTest.kt
@@ -27,7 +27,7 @@
 import androidx.compose.material3.samples.LeadingIconTabs
 import androidx.compose.material3.samples.ScrollingTextTabs
 import androidx.compose.material3.samples.TextTabs
-import androidx.compose.material3.tokens.PrimaryNavigationTabTokens
+import androidx.compose.material3.tokens.DividerTokens
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
@@ -326,9 +326,9 @@
         rule.onNodeWithTag("divider", true)
             .assertPositionInRootIsEqualTo(
                 expectedLeft = 0.dp,
-                expectedTop = tabRowBounds.height - PrimaryNavigationTabTokens.DividerHeight
+                expectedTop = tabRowBounds.height - DividerTokens.Thickness
             )
-            .assertHeightIsEqualTo(PrimaryNavigationTabTokens.DividerHeight)
+            .assertHeightIsEqualTo(DividerTokens.Thickness)
     }
 
     @Test
@@ -435,7 +435,7 @@
     }
 
     @Test
-    fun LeadingIconTab_textAndIconPosition() {
+    fun leadingIconTab_textAndIconPosition() {
         rule.setMaterialContent(lightColorScheme()) {
             Box {
                 TabRow(
@@ -564,9 +564,9 @@
         rule.onNodeWithTag("divider", true)
             .assertPositionInRootIsEqualTo(
                 expectedLeft = 0.dp,
-                expectedTop = tabRowBounds.height - PrimaryNavigationTabTokens.DividerHeight,
+                expectedTop = tabRowBounds.height - DividerTokens.Thickness,
             )
-            .assertHeightIsEqualTo(PrimaryNavigationTabTokens.DividerHeight)
+            .assertHeightIsEqualTo(DividerTokens.Thickness)
     }
 
     @Test
@@ -593,8 +593,10 @@
                 TextTabs()
             }
 
+        val nodes = rule.onAllNodes(isSelectable())
+
         // Only the first tab should be selected
-        rule.onAllNodes(isSelectable())
+        nodes
             .assertCountEquals(3)
             .apply {
                 get(0).assertIsSelected()
@@ -603,10 +605,10 @@
             }
 
         // Click the last tab
-        rule.onAllNodes(isSelectable())[2].performClick()
+        nodes[2].performClick()
 
         // Now only the last tab should be selected
-        rule.onAllNodes(isSelectable())
+        nodes
             .assertCountEquals(3)
             .apply {
                 get(0).assertIsNotSelected()
@@ -747,7 +749,7 @@
             val titles = listOf("TAB 1", "TAB 2", "TAB 3 WITH LOTS OF TEXT")
 
             val indicator = @Composable { tabPositions: List<TabPosition> ->
-                TabRowDefaults.Indicator(
+                TabRowDefaults.SecondaryIndicator(
                     Modifier
                         .tabIndicatorOffset(tabPositions[state])
                         .testTag("indicator")
@@ -791,7 +793,7 @@
 
     @Test
     fun testInspectorValue() {
-        val pos = TabPosition(10.0.dp, 200.0.dp)
+        val pos = TabPosition(10.0.dp, 200.0.dp, 0.dp)
         rule.setContent {
             val modifier = Modifier.tabIndicatorOffset(pos) as InspectableValue
             assertThat(modifier.nameFallback).isEqualTo("tabIndicatorOffset")
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tab.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tab.kt
index d726302..31ab35e 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tab.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tab.kt
@@ -315,7 +315,11 @@
                 ) { text() }
             }
             if (icon != null) {
-                Box(Modifier.layoutId("icon")) { icon() }
+                Box(
+                    Modifier
+                        .layoutId("icon")
+                        .padding(horizontal = HorizontalTextPadding)
+                ) { icon() }
             }
         }
     ) { measurables, constraints ->
@@ -430,7 +434,7 @@
 private const val TabFadeOutAnimationDuration = 100
 
 // The horizontal padding on the left and right of text
-private val HorizontalTextPadding = 16.dp
+internal val HorizontalTextPadding = 16.dp
 
 // Distance from the top of the indicator to the text baseline when there is one line of text and an
 // icon
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
index 6480116..82d1d85 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
@@ -24,9 +24,11 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.horizontalScroll
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.rememberScrollState
@@ -43,6 +45,8 @@
 import androidx.compose.ui.composed
 import androidx.compose.ui.draw.clipToBounds
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.SubcomposeLayout
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.unit.Constraints
@@ -52,8 +56,9 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 
-// TODO: Provide M3 tab row asset and docs when available.
 /**
+ * <a href="https://m3.material.io/components/tabs/overview" class="external" target="_blank">Material Design tabs</a>
+ *
  * Material Design fixed tabs.
  *
  * Fixed tabs display all tabs in a set simultaneously. They are best for switching between related
@@ -113,7 +118,7 @@
  * matching content color for [containerColor], or to the current [LocalContentColor] if
  * [containerColor] is not a color from the theme.
  * @param indicator the indicator that represents which tab is currently selected. By default this
- * will be a [TabRowDefaults.Indicator], using a [TabRowDefaults.tabIndicatorOffset] modifier to
+ * will be a [TabRowDefaults.SecondaryIndicator], using a [TabRowDefaults.tabIndicatorOffset] modifier to
  * animate its position. Note that this indicator will be forced to fill up the entire tab row, so
  * you should use [TabRowDefaults.tabIndicatorOffset] or similar to animate the actual drawn
  * indicator inside this space, and provide an offset from the start.
@@ -130,7 +135,7 @@
     contentColor: Color = TabRowDefaults.contentColor,
     indicator: @Composable (tabPositions: List<TabPosition>) -> Unit = @Composable { tabPositions ->
         if (selectedTabIndex < tabPositions.size) {
-            TabRowDefaults.Indicator(
+            TabRowDefaults.SecondaryIndicator(
                 Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
             )
         }
@@ -140,6 +145,18 @@
     },
     tabs: @Composable () -> Unit
 ) {
+    TabRowImpl(modifier, containerColor, contentColor, indicator, divider, tabs)
+}
+
+@Composable
+private fun TabRowImpl(
+    modifier: Modifier,
+    containerColor: Color,
+    contentColor: Color,
+    indicator: @Composable (tabPositions: List<TabPosition>) -> Unit,
+    divider: @Composable () -> Unit,
+    tabs: @Composable () -> Unit
+) {
     Surface(
         modifier = modifier.selectableGroup(),
         color = containerColor,
@@ -169,7 +186,10 @@
             }
 
             val tabPositions = List(tabCount) { index ->
-                TabPosition(tabWidth.toDp() * index, tabWidth.toDp())
+                var contentWidth =
+                    minOf(tabMeasurables[index].maxIntrinsicWidth(tabRowHeight), tabWidth).toDp()
+                contentWidth -= HorizontalTextPadding * 2
+                TabPosition(tabWidth.toDp() * index, tabWidth.toDp(), contentWidth)
             }
 
             layout(tabRowWidth, tabRowHeight) {
@@ -192,8 +212,9 @@
     }
 }
 
-// TODO: Provide M3 tab row asset and docs when available.
 /**
+ * <a href="https://m3.material.io/components/tabs/overview" class="external" target="_blank">Material Design tabs</a>
+ *
  * Material Design scrollable tabs.
  *
  * When a set of tabs cannot fit on screen, use scrollable tabs. Scrollable tabs can use longer text
@@ -215,7 +236,7 @@
  * and the tabs inside the row. This padding helps inform the user that this tab row can be
  * scrolled, unlike a [TabRow].
  * @param indicator the indicator that represents which tab is currently selected. By default this
- * will be a [TabRowDefaults.Indicator], using a [TabRowDefaults.tabIndicatorOffset] modifier to
+ * will be a [TabRowDefaults.SecondaryIndicator], using a [TabRowDefaults.tabIndicatorOffset] modifier to
  * animate its position. Note that this indicator will be forced to fill up the entire tab row, so
  * you should use [TabRowDefaults.tabIndicatorOffset] or similar to animate the actual drawn
  * indicator inside this space, and provide an offset from the start.
@@ -232,7 +253,7 @@
     contentColor: Color = TabRowDefaults.contentColor,
     edgePadding: Dp = ScrollableTabRowPadding,
     indicator: @Composable (tabPositions: List<TabPosition>) -> Unit = @Composable { tabPositions ->
-        TabRowDefaults.Indicator(
+        TabRowDefaults.SecondaryIndicator(
             Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
         )
     },
@@ -276,8 +297,20 @@
                 minHeight = layoutHeight,
                 maxHeight = layoutHeight,
             )
-            val tabPlaceables = tabMeasurables
-                .map { it.measure(tabConstraints) }
+
+            val tabPlaceables = mutableListOf<Placeable>()
+            val tabContentWidths = mutableListOf<Dp>()
+            tabMeasurables.forEach {
+                val placeable = it.measure(tabConstraints)
+                var contentWidth =
+                    minOf(
+                        it.maxIntrinsicWidth(placeable.height),
+                        placeable.width
+                    ).toDp()
+                contentWidth -= HorizontalTextPadding * 2
+                tabPlaceables.add(placeable)
+                tabContentWidths.add(contentWidth)
+            }
 
             val layoutWidth = tabPlaceables.fold(initial = padding * 2) { curr, measurable ->
                 curr + measurable.width
@@ -288,10 +321,16 @@
                 // Place the tabs
                 val tabPositions = mutableListOf<TabPosition>()
                 var left = padding
-                tabPlaceables.forEach {
-                    it.placeRelative(left, 0)
-                    tabPositions.add(TabPosition(left = left.toDp(), width = it.width.toDp()))
-                    left += it.width
+                tabPlaceables.forEachIndexed { index, placeable ->
+                    placeable.placeRelative(left, 0)
+                    tabPositions.add(
+                        TabPosition(
+                            left = left.toDp(),
+                            width = placeable.width.toDp(),
+                            contentWidth = tabContentWidths[index]
+                        )
+                    )
+                    left += placeable.width
                 }
 
                 // The divider is measured with its own height, and width equal to the total width
@@ -333,9 +372,11 @@
  * @property left the left edge's x position from the start of the [TabRow]
  * @property right the right edge's x position from the start of the [TabRow]
  * @property width the width of this tab
+ * @property contentWidth the content width of this tab
  */
 @Immutable
-class TabPosition internal constructor(val left: Dp, val width: Dp) {
+class TabPosition internal constructor(val left: Dp, val width: Dp, val contentWidth: Dp) {
+
     val right: Dp get() = left + width
 
     override fun equals(other: Any?): Boolean {
@@ -344,6 +385,7 @@
 
         if (left != other.left) return false
         if (width != other.width) return false
+        if (contentWidth != other.contentWidth) return false
 
         return true
     }
@@ -351,11 +393,12 @@
     override fun hashCode(): Int {
         var result = left.hashCode()
         result = 31 * result + width.hashCode()
+        result = 31 * result + contentWidth.hashCode()
         return result
     }
 
     override fun toString(): String {
-        return "TabPosition(left=$left, right=$right, width=$width)"
+        return "TabPosition(left=$left, right=$right, width=$width, contentWidth=$contentWidth)"
     }
 }
 
@@ -364,12 +407,14 @@
  */
 object TabRowDefaults {
     /** Default container color of a tab row. */
-    val containerColor: Color @Composable get() =
-        PrimaryNavigationTabTokens.ContainerColor.toColor()
+    val containerColor: Color
+        @Composable get() =
+            PrimaryNavigationTabTokens.ContainerColor.toColor()
 
     /** Default content color of a tab row. */
-    val contentColor: Color @Composable get() =
-        PrimaryNavigationTabTokens.ActiveLabelTextColor.toColor()
+    val contentColor: Color
+        @Composable get() =
+            PrimaryNavigationTabTokens.ActiveLabelTextColor.toColor()
 
     /**
      * Default indicator, which will be positioned at the bottom of the [TabRow], on top of the
@@ -380,6 +425,12 @@
      * @param color color of the indicator
      */
     @Composable
+    @Deprecated(
+        message = "Use SecondaryIndicator instead.",
+        replaceWith = ReplaceWith(
+            "SecondaryIndicator(modifier, height, color)"
+        )
+    )
     fun Indicator(
         modifier: Modifier = Modifier,
         height: Dp = PrimaryNavigationTabTokens.ActiveIndicatorHeight,
@@ -395,6 +446,54 @@
     }
 
     /**
+     * Primary indicator, which will be positioned at the bottom of the [TabRow], on top of the
+     * divider.
+     *
+     * @param modifier modifier for the indicator's layout
+     * @param width width of the indicator
+     * @param height height of the indicator
+     * @param color color of the indicator
+     * @param shape shape of the indicator
+     */
+    @Composable
+    fun PrimaryIndicator(
+        modifier: Modifier = Modifier,
+        width: Dp = 0.dp,
+        height: Dp = PrimaryNavigationTabTokens.ActiveIndicatorHeight,
+        color: Color = PrimaryNavigationTabTokens.ActiveIndicatorColor.toColor(),
+        shape: Shape = PrimaryNavigationTabTokens.ActiveIndicatorShape
+    ) {
+        Spacer(
+            modifier
+                .requiredSize(width, height)
+                .background(color = color, shape = shape)
+        )
+    }
+
+    /**
+     * Secondary indicator, which will be positioned at the bottom of the [TabRow], on top of the
+     * divider.
+     *
+     * @param modifier modifier for the indicator's layout
+     * @param height height of the indicator
+     * @param color color of the indicator
+     */
+    @Composable
+    fun SecondaryIndicator(
+        modifier: Modifier = Modifier,
+        height: Dp = PrimaryNavigationTabTokens.ActiveIndicatorHeight,
+        color: Color =
+            MaterialTheme.colorScheme.fromToken(PrimaryNavigationTabTokens.ActiveIndicatorColor)
+    ) {
+        Box(
+            modifier
+                .fillMaxWidth()
+                .height(height)
+                .background(color = color)
+        )
+    }
+
+    /**
      * [Modifier] that takes up all the available width inside the [TabRow], and then animates
      * the offset of the indicator it is applied to, depending on the [currentTabPosition].
      *
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/PrimaryNavigationTabTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/PrimaryNavigationTabTokens.kt
index 3fd0e91..4622e75 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/PrimaryNavigationTabTokens.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/PrimaryNavigationTabTokens.kt
@@ -13,7 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// VERSION: v0_103
+
+// VERSION: v0_162
 // GENERATED CODE - DO NOT MODIFY BY HAND
 
 package androidx.compose.material3.tokens
@@ -29,8 +30,6 @@
     val ContainerElevation = ElevationTokens.Level0
     val ContainerHeight = 48.0.dp
     val ContainerShape = ShapeKeyTokens.CornerNone
-    val DividerColor = ColorSchemeKeyTokens.SurfaceVariant
-    val DividerHeight = 1.0.dp
     val ActiveFocusIconColor = ColorSchemeKeyTokens.Primary
     val ActiveHoverIconColor = ColorSchemeKeyTokens.Primary
     val ActiveIconColor = ColorSchemeKeyTokens.Primary
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SecondaryNavigationTabTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SecondaryNavigationTabTokens.kt
new file mode 100644
index 0000000..6d34a48
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/SecondaryNavigationTabTokens.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+// VERSION: v0_162
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.compose.material3.tokens
+
+import androidx.compose.ui.unit.dp
+
+internal object SecondaryNavigationTabTokens {
+    val ActiveLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val ContainerColor = ColorSchemeKeyTokens.Surface
+    val ContainerElevation = ElevationTokens.Level0
+    val ContainerHeight = 48.0.dp
+    val ContainerShape = ShapeKeyTokens.CornerNone
+    val DividerColor = ColorSchemeKeyTokens.SurfaceVariant
+    val DividerHeight = 1.0.dp
+    val FocusLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val HoverLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val InactiveLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val LabelTextFont = TypographyKeyTokens.TitleSmall
+    val PressedLabelTextColor = ColorSchemeKeyTokens.OnSurface
+    val ActiveIconColor = ColorSchemeKeyTokens.OnSurface
+    val FocusIconColor = ColorSchemeKeyTokens.OnSurface
+    val HoverIconColor = ColorSchemeKeyTokens.OnSurface
+    val IconSize = 24.0.dp
+    val InactiveIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
+    val PressedIconColor = ColorSchemeKeyTokens.OnSurface
+}
\ No newline at end of file
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt
index 1254875..9efcc11 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt
@@ -22,6 +22,7 @@
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.launch
 import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CancellationException
 
 /**
  * Schedule [effect] to run when the current composition completes successfully and applies
@@ -284,17 +285,18 @@
     private var job: Job? = null
 
     override fun onRemembered() {
+        // This should never happen but is left here for safety
         job?.cancel("Old job was still running!")
         job = scope.launch(block = task)
     }
 
     override fun onForgotten() {
-        job?.cancel()
+        job?.cancel(LeftCompositionCancellationException())
         job = null
     }
 
     override fun onAbandoned() {
-        job?.cancel()
+        job?.cancel(LeftCompositionCancellationException())
         job = null
     }
 }
@@ -384,6 +386,12 @@
     remember(key1, key2, key3) { LaunchedEffectImpl(applyContext, block) }
 }
 
+private class LeftCompositionCancellationException : CancellationException(
+    "The coroutine scope left the composition"
+) {
+    override fun fillInStackTrace(): Throwable = this
+}
+
 /**
  * When [LaunchedEffect] enters the composition it will launch [block] into the composition's
  * [CoroutineContext]. The coroutine will be [cancelled][Job.cancel] and **re-launched** when
@@ -416,11 +424,11 @@
     }
 
     override fun onForgotten() {
-        coroutineScope.cancel()
+        coroutineScope.cancel(LeftCompositionCancellationException())
     }
 
     override fun onAbandoned() {
-        coroutineScope.cancel()
+        coroutineScope.cancel(LeftCompositionCancellationException())
     }
 }
 
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
index b66eab9..faf8739 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Recomposer.kt
@@ -614,7 +614,7 @@
                         try {
                             // We could do toComplete += toApply but doing it like below
                             // avoids unncessary allocations since toApply is a mutable list
-                            toComplete += toApply
+                            // toComplete += toApply
                             toApply.fastForEach { composition ->
                                 toComplete.add(composition)
                             }
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/build.gradle b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/build.gradle
index 5824041..488e08f 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/build.gradle
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/build.gradle
@@ -14,11 +14,9 @@
  * limitations under the License.
  */
 
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
 plugins {
     id("AndroidXPlugin")
-    id("com.android.library")
+    id("com.android.test")
     id("kotlin-android")
 }
 
@@ -30,26 +28,23 @@
 
     // We need animations to work for MotionLayout
     testOptions.animationsDisabled  false
+    targetProjectPath = ":constraintlayout:constraintlayout-compose:integration-tests:macrobenchmark-target"
+    experimentalProperties["android.experimental.self-instrumenting"] = true
 }
 
+// Create a release build type and make sure it's the only one enabled.
+// This is needed because we benchmark the release build type only.
+android.buildTypes { release {} }
+androidComponents { beforeVariants(selector().all()) { enabled = buildType == 'release' } }
+
 dependencies {
-    androidTestImplementation(project(":benchmark:benchmark-junit4"))
-    androidTestImplementation(project(":benchmark:benchmark-macro-junit4"))
-    androidTestImplementation(project(":internal-testutils-macrobenchmark"))
-    androidTestImplementation(project(":internal-testutils-runtime"))
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testUiautomator)
+    implementation(project(":benchmark:benchmark-junit4"))
+    implementation(project(":benchmark:benchmark-macro-junit4"))
+    implementation(project(":internal-testutils-macrobenchmark"))
+    implementation(project(":internal-testutils-runtime"))
+    implementation(libs.testRules)
+    implementation(libs.testExtJunit)
+    implementation(libs.testCore)
+    implementation(libs.testRunner)
+    implementation(libs.testUiautomator)
 }
-
-// Define a task dependency so the app is installed before we run macro benchmarks.
-afterEvaluate {
-    tasks.getByPath(":constraintlayout:constraintlayout-compose:integration-tests:macrobenchmark:connectedDebugAndroidTest")
-            .dependsOn(
-                    tasks.getByPath(
-                            ":constraintlayout:constraintlayout-compose:integration-tests:macrobenchmark-target:installRelease"
-                    )
-            )
-}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8e90956
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest />
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt
similarity index 100%
rename from constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt
rename to constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt
diff --git a/emoji2/integration-tests/init-disabled-macrobenchmark/build.gradle b/emoji2/integration-tests/init-disabled-macrobenchmark/build.gradle
index 708a1b6..4a65042 100644
--- a/emoji2/integration-tests/init-disabled-macrobenchmark/build.gradle
+++ b/emoji2/integration-tests/init-disabled-macrobenchmark/build.gradle
@@ -16,7 +16,7 @@
 
 plugins {
     id("AndroidXPlugin")
-    id("com.android.library")
+    id("com.android.test")
     id("kotlin-android")
 }
 
@@ -25,23 +25,20 @@
         minSdkVersion 23
     }
     namespace "androidx.emoji2.integration.macrobenchmark.disabled"
+    targetProjectPath = ":emoji2:integration-tests:init-disabled-macrobenchmark-target"
+    experimentalProperties["android.experimental.self-instrumenting"] = true
 }
 
+// Create a release build type and make sure it's the only one enabled.
+// This is needed because we benchmark the release build type only.
+android.buildTypes { release {} }
+androidComponents { beforeVariants(selector().all()) { enabled = buildType == 'release' } }
+
 dependencies {
-    androidTestImplementation(project(":benchmark:benchmark-macro-junit4"))
-    androidTestImplementation(project(":internal-testutils-macrobenchmark"))
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-}
-
-// Define a task dependency so the app is installed before we run macro benchmarks.
-afterEvaluate {
-    tasks.getByPath(":emoji2:integration-tests:init-disabled-macrobenchmark:connectedDebugAndroidTest")
-            .dependsOn(
-                    tasks.getByPath(
-                            ":emoji2:integration-tests:init-disabled-macrobenchmark-target:installRelease"
-                    )
-            )
+    implementation(project(":benchmark:benchmark-macro-junit4"))
+    implementation(project(":internal-testutils-macrobenchmark"))
+    implementation(libs.testRules)
+    implementation(libs.testExtJunit)
+    implementation(libs.testCore)
+    implementation(libs.testRunner)
 }
diff --git a/benchmark/integration-tests/test-module-sample/src/main/AndroidManifest.xml b/emoji2/integration-tests/init-disabled-macrobenchmark/src/main/AndroidManifest.xml
similarity index 60%
rename from benchmark/integration-tests/test-module-sample/src/main/AndroidManifest.xml
rename to emoji2/integration-tests/init-disabled-macrobenchmark/src/main/AndroidManifest.xml
index 37fa920..5b41847 100644
--- a/benchmark/integration-tests/test-module-sample/src/main/AndroidManifest.xml
+++ b/emoji2/integration-tests/init-disabled-macrobenchmark/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ Copyright (C) 2020 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -14,12 +14,4 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-    <queries>
-        <!--
-        Enables querying application info with packageManager.getApplicationInfo, to verify
-        the target package is present
-        -->
-        <package android:name="androidx.benchmark.integration.macrobenchmark.target" />
-    </queries>
-</manifest>
+<manifest />
diff --git a/emoji2/integration-tests/init-disabled-macrobenchmark/src/androidTest/java/androidx/emoji2/integration/macrobenchmark/disabled/EmojiStartupBenchmark.kt b/emoji2/integration-tests/init-disabled-macrobenchmark/src/main/java/androidx/emoji2/integration/macrobenchmark/disabled/EmojiStartupBenchmark.kt
similarity index 100%
rename from emoji2/integration-tests/init-disabled-macrobenchmark/src/androidTest/java/androidx/emoji2/integration/macrobenchmark/disabled/EmojiStartupBenchmark.kt
rename to emoji2/integration-tests/init-disabled-macrobenchmark/src/main/java/androidx/emoji2/integration/macrobenchmark/disabled/EmojiStartupBenchmark.kt
diff --git a/emoji2/integration-tests/init-enabled-macrobenchmark/build.gradle b/emoji2/integration-tests/init-enabled-macrobenchmark/build.gradle
index 1dee7de..c1f5ed9 100644
--- a/emoji2/integration-tests/init-enabled-macrobenchmark/build.gradle
+++ b/emoji2/integration-tests/init-enabled-macrobenchmark/build.gradle
@@ -16,7 +16,7 @@
 
 plugins {
     id("AndroidXPlugin")
-    id("com.android.library")
+    id("com.android.test")
     id("kotlin-android")
 }
 
@@ -25,24 +25,21 @@
         minSdkVersion 23
     }
     namespace "androidx.emoji2.integration.macrobenchmark.enabled"
+    targetProjectPath = ":emoji2:integration-tests:init-enabled-macrobenchmark-target"
+    experimentalProperties["android.experimental.self-instrumenting"] = true
 }
 
+// Create a release build type and make sure it's the only one enabled.
+// This is needed because we benchmark the release build type only.
+android.buildTypes { release {} }
+androidComponents { beforeVariants(selector().all()) { enabled = buildType == 'release' } }
+
 dependencies {
-    androidTestImplementation(project(":emoji2:emoji2"))
-    androidTestImplementation(project(":benchmark:benchmark-macro-junit4"))
-    androidTestImplementation(project(":internal-testutils-macrobenchmark"))
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-}
-
-// Define a task dependency so the app is installed before we run macro benchmarks.
-afterEvaluate {
-    tasks.getByPath(":emoji2:integration-tests:init-enabled-macrobenchmark:connectedDebugAndroidTest")
-            .dependsOn(
-                    tasks.getByPath(
-                            ":emoji2:integration-tests:init-enabled-macrobenchmark-target:installRelease"
-                    )
-            )
+    implementation(project(":emoji2:emoji2"))
+    implementation(project(":benchmark:benchmark-macro-junit4"))
+    implementation(project(":internal-testutils-macrobenchmark"))
+    implementation(libs.testRules)
+    implementation(libs.testExtJunit)
+    implementation(libs.testCore)
+    implementation(libs.testRunner)
 }
diff --git a/benchmark/integration-tests/test-module-sample/src/main/AndroidManifest.xml b/emoji2/integration-tests/init-enabled-macrobenchmark/src/main/AndroidManifest.xml
similarity index 60%
copy from benchmark/integration-tests/test-module-sample/src/main/AndroidManifest.xml
copy to emoji2/integration-tests/init-enabled-macrobenchmark/src/main/AndroidManifest.xml
index 37fa920..5b41847 100644
--- a/benchmark/integration-tests/test-module-sample/src/main/AndroidManifest.xml
+++ b/emoji2/integration-tests/init-enabled-macrobenchmark/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ Copyright (C) 2020 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -14,12 +14,4 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-    <queries>
-        <!--
-        Enables querying application info with packageManager.getApplicationInfo, to verify
-        the target package is present
-        -->
-        <package android:name="androidx.benchmark.integration.macrobenchmark.target" />
-    </queries>
-</manifest>
+<manifest />
diff --git a/emoji2/integration-tests/init-enabled-macrobenchmark/src/androidTest/java/androidx/emoji2/integration/macrobenchmark/enabled/EmojiStartupBenchmark.kt b/emoji2/integration-tests/init-enabled-macrobenchmark/src/main/java/androidx/emoji2/integration/macrobenchmark/enabled/EmojiStartupBenchmark.kt
similarity index 100%
rename from emoji2/integration-tests/init-enabled-macrobenchmark/src/androidTest/java/androidx/emoji2/integration/macrobenchmark/enabled/EmojiStartupBenchmark.kt
rename to emoji2/integration-tests/init-enabled-macrobenchmark/src/main/java/androidx/emoji2/integration/macrobenchmark/enabled/EmojiStartupBenchmark.kt
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/build.gradle b/glance/glance-appwidget/integration-tests/macrobenchmark/build.gradle
index 19b0df6..cfa7f61 100644
--- a/glance/glance-appwidget/integration-tests/macrobenchmark/build.gradle
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/build.gradle
@@ -16,31 +16,37 @@
 
 plugins {
     id("AndroidXPlugin")
-    id("com.android.library")
+    id("com.android.test")
     id("kotlin-android")
 }
 
 android {
+    defaultConfig {
+        minSdkVersion 23
+    }
     namespace "androidx.glance.appwidget.macrobenchmark"
+    targetProjectPath = ":glance:glance-appwidget:integration-tests:macrobenchmark-target"
+    experimentalProperties["android.experimental.self-instrumenting"] = true
 }
 
-android.defaultConfig {
-    minSdkVersion 23
-}
+// Create a release build type and make sure it's the only one enabled.
+// This is needed because we benchmark the release build type only.
+android.buildTypes { release {} }
+androidComponents { beforeVariants(selector().all()) { enabled = buildType == 'release' } }
 
 dependencies {
     implementation 'androidx.compose.ui:ui-unit:1.2.1'
-    androidTestImplementation(project(':benchmark:benchmark-macro'))
-    androidTestImplementation(project(':benchmark:benchmark-common'))
-    androidTestImplementation(project(':benchmark:benchmark-macro-junit4'))
-    androidTestImplementation('androidx.core:core-ktx:1.7.0')
-    androidTestImplementation(project(":glance:glance"))
-    androidTestImplementation(project(":glance:glance-appwidget"))
-    androidTestImplementation(project(":internal-testutils-macrobenchmark"))
-    androidTestImplementation(libs.kotlinTest)
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testUiautomator)
+    implementation(project(':benchmark:benchmark-macro'))
+    implementation(project(':benchmark:benchmark-common'))
+    implementation(project(':benchmark:benchmark-macro-junit4'))
+    implementation('androidx.core:core-ktx:1.7.0')
+    implementation(project(":glance:glance"))
+    implementation(project(":glance:glance-appwidget"))
+    implementation(project(":internal-testutils-macrobenchmark"))
+    implementation(libs.kotlinTest)
+    implementation(libs.testRules)
+    implementation(libs.testExtJunit)
+    implementation(libs.testCore)
+    implementation(libs.testRunner)
+    implementation(libs.testUiautomator)
 }
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml b/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
similarity index 99%
rename from glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
rename to glance/glance-appwidget/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
index 05a1d72..e7eaa978 100644
--- a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/AndroidManifest.xml
+++ b/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
@@ -25,4 +25,4 @@
             android:configChanges="orientation|screenLayout|screenSize"
             android:exported="true"/>
     </application>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostRule.kt b/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostRule.kt
similarity index 100%
rename from glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostRule.kt
rename to glance/glance-appwidget/integration-tests/macrobenchmark/src/main/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostRule.kt
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostTestActivity.kt b/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostTestActivity.kt
similarity index 100%
rename from glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostTestActivity.kt
rename to glance/glance-appwidget/integration-tests/macrobenchmark/src/main/java/androidx/glance/appwidget/macrobenchmark/AppWidgetHostTestActivity.kt
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetUpdateBenchmark.kt b/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/java/androidx/glance/appwidget/macrobenchmark/AppWidgetUpdateBenchmark.kt
similarity index 100%
rename from glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/java/androidx/glance/appwidget/macrobenchmark/AppWidgetUpdateBenchmark.kt
rename to glance/glance-appwidget/integration-tests/macrobenchmark/src/main/java/androidx/glance/appwidget/macrobenchmark/AppWidgetUpdateBenchmark.kt
diff --git a/glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/res/layout/app_widget_host_activity.xml b/glance/glance-appwidget/integration-tests/macrobenchmark/src/main/res/layout/app_widget_host_activity.xml
similarity index 100%
rename from glance/glance-appwidget/integration-tests/macrobenchmark/src/androidTest/res/layout/app_widget_host_activity.xml
rename to glance/glance-appwidget/integration-tests/macrobenchmark/src/main/res/layout/app_widget_host_activity.xml
diff --git a/libraryversions.toml b/libraryversions.toml
index e865369..413f3f3 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -21,7 +21,7 @@
 COLLECTION = "1.3.0-alpha05"
 COMPOSE = "1.5.0-alpha04"
 COMPOSE_COMPILER = "1.4.7"
-COMPOSE_MATERIAL3 = "1.2.0-alpha01"
+COMPOSE_MATERIAL3 = "1.2.0-alpha02"
 COMPOSE_MATERIAL3_ADAPTIVE = "1.0.0-alpha01"
 COMPOSE_RUNTIME_TRACING = "1.0.0-alpha03"
 CONSTRAINTLAYOUT = "2.2.0-alpha10"
diff --git a/profileinstaller/integration-tests/init-macrobenchmark/build.gradle b/profileinstaller/integration-tests/init-macrobenchmark/build.gradle
index 0d054c7..7088271 100644
--- a/profileinstaller/integration-tests/init-macrobenchmark/build.gradle
+++ b/profileinstaller/integration-tests/init-macrobenchmark/build.gradle
@@ -16,7 +16,7 @@
 
 plugins {
     id("AndroidXPlugin")
-    id("com.android.library")
+    id("com.android.test")
     id("kotlin-android")
 }
 
@@ -25,24 +25,21 @@
         minSdkVersion 23
     }
     namespace "androidx.profileinstaller.integration.macrobenchmark"
+    targetProjectPath = ":profileinstaller:integration-tests:init-macrobenchmark-target"
+    experimentalProperties["android.experimental.self-instrumenting"] = true
 }
 
+// Create a release build type and make sure it's the only one enabled.
+// This is needed because we benchmark the release build type only.
+android.buildTypes { release {} }
+androidComponents { beforeVariants(selector().all()) { enabled = buildType == 'release' } }
+
 dependencies {
-    androidTestImplementation(project(":profileinstaller:profileinstaller"))
-    androidTestImplementation(project(":benchmark:benchmark-macro-junit4"))
-    androidTestImplementation(project(":internal-testutils-macrobenchmark"))
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-}
-
-// Define a task dependency so the app is installed before we run macro benchmarks.
-afterEvaluate {
-    tasks.getByPath(":profileinstaller:integration-tests:init-macrobenchmark:connectedDebugAndroidTest")
-            .dependsOn(
-                    tasks.getByPath(
-                            ":profileinstaller:integration-tests:init-macrobenchmark-target:installRelease"
-                    )
-            )
+    implementation(project(":profileinstaller:profileinstaller"))
+    implementation(project(":benchmark:benchmark-macro-junit4"))
+    implementation(project(":internal-testutils-macrobenchmark"))
+    implementation(libs.testRules)
+    implementation(libs.testExtJunit)
+    implementation(libs.testCore)
+    implementation(libs.testRunner)
 }
diff --git a/benchmark/integration-tests/test-module-sample/src/main/AndroidManifest.xml b/profileinstaller/integration-tests/init-macrobenchmark/src/main/AndroidManifest.xml
similarity index 60%
copy from benchmark/integration-tests/test-module-sample/src/main/AndroidManifest.xml
copy to profileinstaller/integration-tests/init-macrobenchmark/src/main/AndroidManifest.xml
index 37fa920..5b41847 100644
--- a/benchmark/integration-tests/test-module-sample/src/main/AndroidManifest.xml
+++ b/profileinstaller/integration-tests/init-macrobenchmark/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ Copyright (C) 2020 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -14,12 +14,4 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
-    <queries>
-        <!--
-        Enables querying application info with packageManager.getApplicationInfo, to verify
-        the target package is present
-        -->
-        <package android:name="androidx.benchmark.integration.macrobenchmark.target" />
-    </queries>
-</manifest>
+<manifest />
diff --git a/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/java/androidx/profileinstaller/integration/macrobenchmark/ProfileinstallerStartupBenchmark.kt b/profileinstaller/integration-tests/init-macrobenchmark/src/main/java/androidx/profileinstaller/integration/macrobenchmark/ProfileinstallerStartupBenchmark.kt
similarity index 100%
rename from profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/java/androidx/profileinstaller/integration/macrobenchmark/ProfileinstallerStartupBenchmark.kt
rename to profileinstaller/integration-tests/init-macrobenchmark/src/main/java/androidx/profileinstaller/integration/macrobenchmark/ProfileinstallerStartupBenchmark.kt
diff --git a/settings.gradle b/settings.gradle
index f0129a6..0f11a3a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -472,7 +472,6 @@
 includeProject(":benchmark:integration-tests:dry-run-benchmark", [BuildType.MAIN])
 includeProject(":benchmark:integration-tests:macrobenchmark", [BuildType.MAIN])
 includeProject(":benchmark:integration-tests:macrobenchmark-target", [BuildType.MAIN])
-includeProject(":benchmark:integration-tests:test-module-sample", [BuildType.MAIN])
 includeProject(":benchmark:integration-tests:startup-benchmark", [BuildType.MAIN])
 includeProject(":biometric:biometric", [BuildType.MAIN])
 includeProject(":biometric:biometric-ktx", [BuildType.MAIN])
diff --git a/slice/slice-benchmark/build.gradle b/slice/slice-benchmark/build.gradle
index e353c79..1e151d0 100644
--- a/slice/slice-benchmark/build.gradle
+++ b/slice/slice-benchmark/build.gradle
@@ -42,10 +42,14 @@
 androidx {
     name = "Slices Benchmarks"
     publish = Publish.NONE // Library is deprecated pending removal.
-    disableDeviceTests = true
     mavenVersion = LibraryVersions.SLICE_BENCHMARK
     inceptionYear = "2018"
     description = "RecyclerView Benchmarks"
+
+    deviceTests {
+        // Pending removal, don't run tests.
+        enabled = false
+    }
 }
 
 android {
diff --git a/slice/slice-builders-ktx/build.gradle b/slice/slice-builders-ktx/build.gradle
index 7ef627a..cbd1af6 100644
--- a/slice/slice-builders-ktx/build.gradle
+++ b/slice/slice-builders-ktx/build.gradle
@@ -47,11 +47,15 @@
 androidx {
     name = "Slice builders KTX"
     publish = Publish.SNAPSHOT_ONLY // Library is deprecated pending removal.
-    disableDeviceTests = true // Pending removal, don't run tests.
     runApiTasks = new RunApiTasks.Yes() // Pending removal, but keep API files for now.
     mavenVersion = LibraryVersions.SLICE_BUILDERS_KTX
     inceptionYear = "2018"
     description = "A set of Kotlin extension methods built on top of slice-builders APIs."
     failOnDeprecationWarnings = false
     legacyDisableKotlinStrictApiMode = true
+
+    deviceTests {
+        // Pending removal, don't run tests.
+        enabled = false
+    }
 }
diff --git a/slice/slice-builders/build.gradle b/slice/slice-builders/build.gradle
index d0ba1a4..a8c0dbb 100644
--- a/slice/slice-builders/build.gradle
+++ b/slice/slice-builders/build.gradle
@@ -33,12 +33,16 @@
 androidx {
     name = "Slice builders"
     publish = Publish.SNAPSHOT_ONLY // Library is deprecated pending removal.
-    disableDeviceTests = true // Pending removal, don't run tests.
     runApiTasks = new RunApiTasks.Yes() // Pending removal, but keep API files for now.
     mavenVersion = LibraryVersions.SLICE
     inceptionYear = "2017"
     description = "A set of builders to create templates using SliceProvider APIs"
     failOnDeprecationWarnings = false
+
+    deviceTests {
+        // Pending removal, don't run tests.
+        enabled = false
+    }
 }
 
 android {
diff --git a/slice/slice-core/build.gradle b/slice/slice-core/build.gradle
index 2bab0a9..03a0441 100644
--- a/slice/slice-core/build.gradle
+++ b/slice/slice-core/build.gradle
@@ -39,12 +39,16 @@
 androidx {
     name = "Common utilities for slices"
     publish = Publish.SNAPSHOT_ONLY // Library is deprecated pending removal.
-    disableDeviceTests = true // Pending removal, don't run tests.
     runApiTasks = new RunApiTasks.Yes() // Pending removal, but keep API files for now.
     mavenVersion = LibraryVersions.SLICE
     inceptionYear = "2017"
     description = "The slices core library provides utilities for the slices view and provider libraries"
     failOnDeprecationWarnings = false
+
+    deviceTests {
+        // Pending removal, don't run tests.
+        enabled = false
+    }
 }
 
 android {
diff --git a/slice/slice-remotecallback/build.gradle b/slice/slice-remotecallback/build.gradle
index 56db28d..edd99ea 100644
--- a/slice/slice-remotecallback/build.gradle
+++ b/slice/slice-remotecallback/build.gradle
@@ -39,12 +39,16 @@
 androidx {
     name = "Slice Remote Callback"
     publish = Publish.SNAPSHOT_ONLY // Library is deprecated pending removal.
-    disableDeviceTests = true // Pending removal, don't run tests.
     runApiTasks = new RunApiTasks.Yes() // Pending removal, but keep API files for now.
     mavenVersion = LibraryVersions.SLICE_REMOTECALLBACK
     inceptionYear = "2019"
     description = "A library that handles PendingIntents in slices as remote callbacks"
     failOnDeprecationWarnings = false
+
+    deviceTests {
+        // Pending removal, don't run tests.
+        enabled = false
+    }
 }
 
 android {
diff --git a/slice/slice-test/build.gradle b/slice/slice-test/build.gradle
index ff04df0..a7330d5 100644
--- a/slice/slice-test/build.gradle
+++ b/slice/slice-test/build.gradle
@@ -41,11 +41,15 @@
     name = "Slice test code"
     type = LibraryType.INTERNAL_TEST_LIBRARY
     publish = Publish.NONE // Library is deprecated pending removal.
-    disableDeviceTests = true
     mavenVersion = LibraryVersions.SLICE
     inceptionYear = "2017"
     description = "A library that holds common code for testing slices"
     failOnDeprecationWarnings = false
+
+    deviceTests {
+        // Pending removal, don't run tests.
+        enabled = false
+    }
 }
 
 android {
diff --git a/slice/slice-view/build.gradle b/slice/slice-view/build.gradle
index 5dc4379..8f8c858 100644
--- a/slice/slice-view/build.gradle
+++ b/slice/slice-view/build.gradle
@@ -43,12 +43,16 @@
 androidx {
     name = "Slice views"
     publish = Publish.SNAPSHOT_ONLY // Library is deprecated pending removal.
-    disableDeviceTests = true // Pending removal, don't run tests.
     runApiTasks = new RunApiTasks.Yes() // Pending removal, but keep API files for now.
     mavenVersion = LibraryVersions.SLICE
     inceptionYear = "2017"
     description = "A library that handles rendering of slice content into supported templates"
     failOnDeprecationWarnings = false
+
+    deviceTests {
+        // Pending removal, don't run tests.
+        enabled = false
+    }
 }
 
 android {
diff --git a/wear/benchmark/integration-tests/macrobenchmark/build.gradle b/wear/benchmark/integration-tests/macrobenchmark/build.gradle
index 1708504..e7fc8f1 100644
--- a/wear/benchmark/integration-tests/macrobenchmark/build.gradle
+++ b/wear/benchmark/integration-tests/macrobenchmark/build.gradle
@@ -16,7 +16,7 @@
 
 plugins {
     id("AndroidXPlugin")
-    id 'com.android.library'
+    id 'com.android.test'
     id 'kotlin-android'
 }
 
@@ -25,25 +25,22 @@
         minSdkVersion 29
     }
     namespace "androidx.wear.benchmark.integration.macrobenchmark"
+    targetProjectPath = ":wear:benchmark:integration-tests:macrobenchmark-target"
+    experimentalProperties["android.experimental.self-instrumenting"] = true
 }
 
+// Create a release build type and make sure it's the only one enabled.
+// This is needed because we benchmark the release build type only.
+android.buildTypes { release {} }
+androidComponents { beforeVariants(selector().all()) { enabled = buildType == 'release' } }
+
 dependencies {
-    androidTestImplementation(project(":benchmark:benchmark-junit4"))
-    androidTestImplementation(project(":benchmark:benchmark-macro-junit4"))
-    androidTestImplementation(project(":internal-testutils-macrobenchmark"))
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testUiautomator)
-}
-
-// Define a task dependency so the app is installed before we run macro benchmarks.
-afterEvaluate {
-    tasks.getByPath(":wear:benchmark:integration-tests:macrobenchmark:connectedDebugAndroidTest")
-            .dependsOn(
-                    tasks.getByPath(
-                            ":wear:benchmark:integration-tests:macrobenchmark-target:installRelease"
-                    )
-            )
+    implementation(project(":benchmark:benchmark-junit4"))
+    implementation(project(":benchmark:benchmark-macro-junit4"))
+    implementation(project(":internal-testutils-macrobenchmark"))
+    implementation(libs.testRules)
+    implementation(libs.testExtJunit)
+    implementation(libs.testCore)
+    implementation(libs.testRunner)
+    implementation(libs.testUiautomator)
 }
diff --git a/wear/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/benchmark/integration/macrobenchmark/ScrollBenchmark.kt b/wear/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/wear/benchmark/integration/macrobenchmark/ScrollBenchmark.kt
similarity index 100%
rename from wear/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/benchmark/integration/macrobenchmark/ScrollBenchmark.kt
rename to wear/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/wear/benchmark/integration/macrobenchmark/ScrollBenchmark.kt
diff --git a/wear/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/benchmark/integration/macrobenchmark/StartupBenchmark.kt b/wear/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/wear/benchmark/integration/macrobenchmark/StartupBenchmark.kt
similarity index 100%
rename from wear/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/benchmark/integration/macrobenchmark/StartupBenchmark.kt
rename to wear/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/wear/benchmark/integration/macrobenchmark/StartupBenchmark.kt
diff --git a/wear/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/benchmark/integration/macrobenchmark/SwipeBenchmark.kt b/wear/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/wear/benchmark/integration/macrobenchmark/SwipeBenchmark.kt
similarity index 100%
rename from wear/benchmark/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/benchmark/integration/macrobenchmark/SwipeBenchmark.kt
rename to wear/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/wear/benchmark/integration/macrobenchmark/SwipeBenchmark.kt
diff --git a/wear/compose/integration-tests/macrobenchmark/build.gradle b/wear/compose/integration-tests/macrobenchmark/build.gradle
index 1e07985..414dd2b 100644
--- a/wear/compose/integration-tests/macrobenchmark/build.gradle
+++ b/wear/compose/integration-tests/macrobenchmark/build.gradle
@@ -1,5 +1,3 @@
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
 /*
  * Copyright 2021 The Android Open Source Project
  *
@@ -18,7 +16,7 @@
 
 plugins {
     id("AndroidXPlugin")
-    id("com.android.library")
+    id("com.android.test")
     id("kotlin-android")
 }
 
@@ -27,25 +25,22 @@
         minSdkVersion 29
     }
     namespace "androidx.wear.compose.integration.macrobenchmark"
+    targetProjectPath = ":wear:compose:integration-tests:macrobenchmark-target"
+    experimentalProperties["android.experimental.self-instrumenting"] = true
 }
 
+// Create a release build type and make sure it's the only one enabled.
+// This is needed because we benchmark the release build type only.
+android.buildTypes { release {} }
+androidComponents { beforeVariants(selector().all()) { enabled = buildType == 'release' } }
+
 dependencies {
-    androidTestImplementation(project(":benchmark:benchmark-junit4"))
-    androidTestImplementation(project(":benchmark:benchmark-macro-junit4"))
-    androidTestImplementation(project(":internal-testutils-macrobenchmark"))
-    androidTestImplementation(libs.testRules)
-    androidTestImplementation(libs.testExtJunit)
-    androidTestImplementation(libs.testCore)
-    androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.testUiautomator)
-}
-
-// Define a task dependency so the app is installed before we run macro benchmarks.
-afterEvaluate {
-    tasks.getByPath(":wear:compose:integration-tests:macrobenchmark:connectedDebugAndroidTest")
-            .dependsOn(
-                    tasks.getByPath(
-                            ":wear:compose:integration-tests:macrobenchmark-target:installRelease"
-                    )
-            )
+    implementation(project(":benchmark:benchmark-junit4"))
+    implementation(project(":benchmark:benchmark-macro-junit4"))
+    implementation(project(":internal-testutils-macrobenchmark"))
+    implementation(libs.testRules)
+    implementation(libs.testExtJunit)
+    implementation(libs.testCore)
+    implementation(libs.testRunner)
+    implementation(libs.testUiautomator)
 }
diff --git a/wear/compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml b/wear/compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e0788d6
--- /dev/null
+++ b/wear/compose/integration-tests/macrobenchmark/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest />
diff --git a/wear/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/compose/integration/macrobenchmark/BaselineProfile.kt b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/BaselineProfile.kt
similarity index 100%
rename from wear/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/compose/integration/macrobenchmark/BaselineProfile.kt
rename to wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/BaselineProfile.kt
diff --git a/wear/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/compose/integration/macrobenchmark/Common.kt b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/Common.kt
similarity index 100%
rename from wear/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/compose/integration/macrobenchmark/Common.kt
rename to wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/Common.kt
diff --git a/wear/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/compose/integration/macrobenchmark/ScrollBenchmark.kt b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/ScrollBenchmark.kt
similarity index 100%
rename from wear/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/compose/integration/macrobenchmark/ScrollBenchmark.kt
rename to wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/ScrollBenchmark.kt
diff --git a/wear/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/compose/integration/macrobenchmark/StartupBenchmark.kt b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/StartupBenchmark.kt
similarity index 100%
rename from wear/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/compose/integration/macrobenchmark/StartupBenchmark.kt
rename to wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/StartupBenchmark.kt
diff --git a/wear/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/compose/integration/macrobenchmark/SwipeBenchmark.kt b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/SwipeBenchmark.kt
similarity index 100%
rename from wear/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/compose/integration/macrobenchmark/SwipeBenchmark.kt
rename to wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/SwipeBenchmark.kt