New library compose:runtime:runtime-tracing

When added as a dependency to a Compose application, enables
recomposition tracing:
- declares a dependency on tracing-perfetto
- registers tracing-perfetto with Compose

Remaining steps to trace have to be performed by tooling (e.g. Android
Studio Profiler):
- provide tracing-perfetto-binary dependencies required for tracing
- notify the app to register with Perfetto SDK (using
  androidx.tracing.perfetto.TracingReceiver)
- include track_event as a category in its Perfetto capture config
- capture and present a Perfetto trace

End-to-end test covered by TracingInitializerTest where Macrobenchmark
performs the steps described in the previous paragraph.

Bug: 214560409
Bug: 214560414
Bug: 214562374
Test: ./gradlew :compose:runtime:runtime-tracing:connectedCheck -Pandroid.testInstrumentationRunnerArguments.class=androidx.compose.runtime.tracing.test.TracingInitializerTest
Test: ./gradlew :compose:integration-tests:macrobenchmark-target:installRelease && ./gradlew :compose:integration-tests:macrobenchmark:connectedCheck -Pandroid.testInstrumentationRunnerArguments.class=androidx.compose.integration.macrobenchmark.TrivialTracingBenchmark

Change-Id: Id1ea30fdea39da8d63b8c0963d9e8e351704985b
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoConfig.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoConfig.kt
index 093758b..60d54ed 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoConfig.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoConfig.kt
@@ -170,7 +170,8 @@
         PROCESS_STATS_DATASOURCE,
         LINUX_SYS_STATS_DATASOURCE,
         ANDROID_POWER_DATASOURCE,
-        TraceConfig.DataSource(DataSourceConfig("android.surfaceflinger.frametimeline"))
+        TraceConfig.DataSource(DataSourceConfig("android.surfaceflinger.frametimeline")),
+        TraceConfig.DataSource(DataSourceConfig("track_event")) // required by tracing-perfetto
     ),
     // periodically dump to file, so we don't overrun our ring buffer
     // buffers are expected to be big enough for 5 seconds, so conservatively set 2.5 dump
diff --git a/compose/integration-tests/macrobenchmark-target/build.gradle b/compose/integration-tests/macrobenchmark-target/build.gradle
index 37eacb5..0c1ddaf 100644
--- a/compose/integration-tests/macrobenchmark-target/build.gradle
+++ b/compose/integration-tests/macrobenchmark-target/build.gradle
@@ -32,6 +32,7 @@
     implementation(project(":compose:foundation:foundation"))
     implementation(project(":compose:material:material"))
     implementation(project(":compose:runtime:runtime"))
+    implementation(project(":compose:runtime:runtime-tracing"))
     implementation(project(":compose:ui:ui"))
     implementation(project(":compose:ui:ui-tooling"))
 }
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml b/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
index 333c84e..d997cfc 100644
--- a/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
+++ b/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -103,5 +103,13 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+        <activity
+            android:name=".TrivialTracingActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="androidx.compose.integration.macrobenchmark.target.TRIVIAL_TRACING_ACTIVITY" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/TrivialTracingActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/TrivialTracingActivity.kt
new file mode 100644
index 0000000..00d60904
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/TrivialTracingActivity.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark.target
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.runtime.Composable
+
+class TrivialTracingActivity : ComponentActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setContent {
+            Foo_BBC27C8E_13A7_4A5F_A735_AFDC433F54C3()
+            Baz_609801AB_F5A9_47C3_9405_2E82542F21B8()
+        }
+    }
+}
+
+@Composable
+private fun Foo_BBC27C8E_13A7_4A5F_A735_AFDC433F54C3() =
+    Bar_4888EA32_ABC5_4550_BA78_1247FEC1AAC9()
+
+@Composable
+private fun Bar_4888EA32_ABC5_4550_BA78_1247FEC1AAC9() {
+}
+
+@Composable
+private fun Baz_609801AB_F5A9_47C3_9405_2E82542F21B8() {
+}
\ No newline at end of file
diff --git a/compose/integration-tests/macrobenchmark/build.gradle b/compose/integration-tests/macrobenchmark/build.gradle
index 60103b8..52926c1 100644
--- a/compose/integration-tests/macrobenchmark/build.gradle
+++ b/compose/integration-tests/macrobenchmark/build.gradle
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
 plugins {
     id("AndroidXPlugin")
     id("com.android.library")
@@ -42,3 +44,10 @@
 // Define a task dependency so the app is installed before we run macro benchmarks.
 tasks.getByPath(":compose:integration-tests:macrobenchmark:connectedCheck")
     .dependsOn(tasks.getByPath(":compose:integration-tests:macrobenchmark-target:installRelease"))
+
+// Allow usage of Kotlin's @OptIn.
+tasks.withType(KotlinCompile).configureEach {
+    kotlinOptions {
+        freeCompilerArgs += ["-opt-in=kotlin.RequiresOptIn"]
+    }
+}
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt b/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt
new file mode 100644
index 0000000..2da52f3
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark
+
+import android.content.Intent
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.TraceSectionMetric
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.benchmark.perfetto.PerfettoCapture
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+/**
+ * End-to-end test for compose-runtime-tracing verifying that names of Composables show up in
+ * a Perfetto trace.
+ */
+class TrivialTracingBenchmark {
+    @get:Rule
+    val benchmarkRule = MacrobenchmarkRule()
+
+    @RequiresApi(Build.VERSION_CODES.Q)
+    @OptIn(ExperimentalMetricApi::class)
+    @Test
+    fun test_composable_names_present_in_trace() {
+        val metrics = COMPOSABLE_NAMES.map { composableName ->
+            TraceSectionMetric("%$PACKAGE_NAME.$composableName %$FILE_NAME:% key=%")
+        }
+        benchmarkRule.measureRepeated(
+            packageName = PACKAGE_NAME,
+            metrics = metrics,
+            iterations = 1, // we are only verifying the presence of entries (not the timing data)
+            setupBlock = {
+                PerfettoCapture().enableAndroidxTracingPerfetto(
+                    PACKAGE_NAME,
+                    provideBinariesIfMissing = true
+                )
+            }
+        ) {
+            startActivityAndWait(Intent(ACTION))
+        }
+    }
+
+    companion object {
+        private const val PACKAGE_NAME = "androidx.compose.integration.macrobenchmark.target"
+
+        private const val ACTION =
+            "androidx.compose.integration.macrobenchmark.target.TRIVIAL_TRACING_ACTIVITY"
+
+        private const val FILE_NAME = "TrivialTracingActivity.kt"
+
+        private val COMPOSABLE_NAMES = listOf(
+            "Foo_BBC27C8E_13A7_4A5F_A735_AFDC433F54C3",
+            "Bar_4888EA32_ABC5_4550_BA78_1247FEC1AAC9",
+            "Baz_609801AB_F5A9_47C3_9405_2E82542F21B8"
+        )
+    }
+}
diff --git a/compose/runtime/runtime-tracing/build.gradle b/compose/runtime/runtime-tracing/build.gradle
new file mode 100644
index 0000000..4ed47d9
--- /dev/null
+++ b/compose/runtime/runtime-tracing/build.gradle
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.Publish
+import androidx.build.RunApiTasks
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("kotlin-android")
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 21
+    }
+}
+
+dependencies {
+    api("androidx.annotation:annotation:1.3.0")
+    implementation(libs.kotlinStdlib)
+    implementation(projectOrArtifact(":compose:runtime:runtime"))
+    implementation(projectOrArtifact(":tracing:tracing-perfetto"))
+    implementation("androidx.startup:startup-runtime:1.1.1")
+    androidTestImplementation(libs.testExtJunit)
+    androidTestImplementation(libs.testRunner)
+    androidTestImplementation(libs.truth)
+}
+
+tasks.withType(KotlinCompile).configureEach {
+    kotlinOptions {
+        freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
+    }
+}
+
+androidx {
+    name = "Compose Runtime: Tracing"
+    runApiTasks= new RunApiTasks.No("The API is still evolving") // TODO(b/231444429)
+    publish = Publish.SNAPSHOT_AND_RELEASE
+    mavenVersion = LibraryVersions.COMPOSE_RUNTIME_TRACING
+    mavenGroup = LibraryGroups.COMPOSE_RUNTIME
+    inceptionYear = "2022"
+    description = "Allows for additional tracing in a Compose app (e.g. tracing of Composables taking part in a Recomposition"
+}
diff --git a/compose/runtime/runtime-tracing/src/androidTest/AndroidManifest.xml b/compose/runtime/runtime-tracing/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..0b0410b
--- /dev/null
+++ b/compose/runtime/runtime-tracing/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<!--
+  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.
+  -->
+
+<!--
+  ~ Copyright (C) 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.compose.runtime.tracing.test">
+
+</manifest>
diff --git a/compose/runtime/runtime-tracing/src/androidTest/java/androidx/compose/runtime/tracing/test/TracingInitializerTest.kt b/compose/runtime/runtime-tracing/src/androidTest/java/androidx/compose/runtime/tracing/test/TracingInitializerTest.kt
new file mode 100644
index 0000000..93af8de
--- /dev/null
+++ b/compose/runtime/runtime-tracing/src/androidTest/java/androidx/compose/runtime/tracing/test/TracingInitializerTest.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.runtime.tracing.test
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+/**
+ * Fake test. The module is actually tested inside
+ * [androidx.compose.integration.macrobenchmark.TrivialTracingBenchmark].
+ */
+class TracingInitializerTest {
+    @Test
+    fun two_plus_two() {
+        assertThat<Int>(2 + 2).isEqualTo(4)
+    }
+}
diff --git a/compose/runtime/runtime-tracing/src/main/AndroidManifest.xml b/compose/runtime/runtime-tracing/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b3f90e5
--- /dev/null
+++ b/compose/runtime/runtime-tracing/src/main/AndroidManifest.xml
@@ -0,0 +1,49 @@
+<!--
+  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.
+  -->
+
+<!--
+  ~ Copyright (C) 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="androidx.compose.runtime.tracing">
+
+    <application>
+        <provider
+            android:name="androidx.startup.InitializationProvider"
+            android:authorities="${applicationId}.androidx-startup"
+            android:exported="false"
+            tools:node="merge">
+            <meta-data
+                android:name="androidx.compose.runtime.tracing.TracingInitializer"
+                android:value="androidx.startup" />
+        </provider>
+    </application>
+
+</manifest>
diff --git a/compose/runtime/runtime-tracing/src/main/java/androidx/compose/runtime/tracing/TracingInitializer.kt b/compose/runtime/runtime-tracing/src/main/java/androidx/compose/runtime/tracing/TracingInitializer.kt
new file mode 100644
index 0000000..3721077
--- /dev/null
+++ b/compose/runtime/runtime-tracing/src/main/java/androidx/compose/runtime/tracing/TracingInitializer.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.compose.runtime.tracing
+
+import android.content.Context
+import androidx.compose.runtime.Composer
+import androidx.compose.runtime.CompositionTracer
+import androidx.compose.runtime.InternalComposeTracingApi
+import androidx.startup.Initializer
+import androidx.tracing.perfetto.Tracing
+
+@OptIn(InternalComposeTracingApi::class)
+class TracingInitializer : Initializer<Unit> {
+    override fun create(context: Context) {
+        Composer.setTracer(object : CompositionTracer {
+            override fun traceEventStart(key: Int, dirty1: Int, dirty2: Int, info: String) =
+                Tracing.traceEventStart(key, info)
+
+            override fun traceEventEnd() = Tracing.traceEventEnd()
+
+            override fun isTraceInProgress(): Boolean = Tracing.isEnabled
+        })
+    }
+
+    override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
+}
diff --git a/libraryversions.toml b/libraryversions.toml
index ec0529e..8dd392c 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -20,6 +20,7 @@
 COLLECTION2 = "1.3.0-alpha01"
 COMPOSE = "1.2.0-beta01"
 COMPOSE_MATERIAL3 = "1.0.0-alpha11"
+COMPOSE_RUNTIME_TRACING = "1.0.0-alpha01"
 CONTENTPAGER = "1.1.0-alpha01"
 COORDINATORLAYOUT = "1.3.0-alpha01"
 CORE = "1.8.0-rc01"
diff --git a/settings.gradle b/settings.gradle
index e669502..da82136 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -458,6 +458,7 @@
 includeProject(":compose:runtime:runtime-lint", [BuildType.COMPOSE, BuildType.MAIN])
 includeProject(":compose:runtime:runtime-livedata", [BuildType.COMPOSE])
 includeProject(":compose:runtime:runtime-livedata:runtime-livedata-samples", "compose/runtime/runtime-livedata/samples", [BuildType.COMPOSE])
+includeProject(":compose:runtime:runtime-tracing", [BuildType.COMPOSE])
 includeProject(":compose:runtime:runtime-rxjava2", [BuildType.COMPOSE])
 includeProject(":compose:runtime:runtime-rxjava2:runtime-rxjava2-samples", "compose/runtime/runtime-rxjava2/samples", [BuildType.COMPOSE])
 includeProject(":compose:runtime:runtime-rxjava3", [BuildType.COMPOSE])