Merge "Enable perfetto capture testing back to API 21/23" into androidx-main
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
index cfa0050..17134b9 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
@@ -33,7 +33,7 @@
 import kotlin.test.assertTrue
 
 @LargeTest
-@SdkSuppress(minSdkVersion = 28)
+@SdkSuppress(minSdkVersion = 21)
 @RunWith(AndroidJUnit4::class)
 class PerfettoHelperTest {
     @Before
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
index 83b1cb1..1e3b309 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
@@ -36,6 +36,26 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 object Shell {
+    /**
+     * Returns true if the line from ps output contains the given process/package name.
+     *
+     * NOTE: On API 25 and earlier, the processName of unbundled executables will include the
+     * relative path they were invoked from:
+     *
+     * ```
+     * root      10065 10061 14848  3932  poll_sched 7bcaf1fc8c S /data/local/tmp/tracebox
+     * root      10109 1     11552  1140  poll_sched 78c86eac8c S ./tracebox
+     * ```
+     *
+     * On higher API levels, the process name will simply be e.g. "tracebox".
+     *
+     * As this function is also used for package names (which never have a leading `/`), we
+     * simply check for either.
+     */
+    private fun psLineContainsProcess(psOutputLine: String, processName: String): Boolean {
+        return psOutputLine.endsWith(" $processName") || psOutputLine.endsWith("/$processName")
+    }
+
     fun connectUiAutomation() {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
             ShellImpl // force initialization
@@ -213,7 +233,7 @@
         return executeScript("ps | grep $processName")
             .split(Regex("\r?\n"))
             .map { it.trim() }
-            .filter { it.endsWith(" $processName") }
+            .filter { psLineContainsProcess(psOutputLine = it, processName = processName) }
             .map {
                 // map to int - split, and take 2nd column (PID)
                 it.split(Regex("\\s+"))[1]
@@ -230,7 +250,7 @@
     fun isProcessAlive(pid: Int, processName: String): Boolean {
         return executeCommand("ps $pid")
             .split(Regex("\r?\n"))
-            .any { it.trim().endsWith(" $processName") }
+            .any { psLineContainsProcess(psOutputLine = it, processName = processName) }
     }
 
     @RequiresApi(21)
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
index 21fb7ce..5d5e266a 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
@@ -160,7 +160,7 @@
      *
      * @return true if perfetto is stopped successfully.
      */
-    public fun stopPerfetto() {
+    private fun stopPerfetto() {
         val pid = perfettoPid
 
         require(pid != null)
@@ -187,7 +187,7 @@
             // Unbundled perfetto can read configuration from a file that it has permissions to
             // read from. This because it assumes the identity of the shell and therefore has
             // access to /data/local/tmp directory.
-            "$UNBUNDLED_ENV_PREFIX $unbundledPerfettoShellPath --background" +
+            "$unbundledPerfettoShellPath --background" +
                 " -c $configFilePath" +
                 " -o $outputPath"
         }
@@ -290,13 +290,6 @@
         private const val UNBUNDLED_TEMP_OUTPUT_FILE =
             "$UNBUNDLED_PERFETTO_ROOT_DIR/trace_output.pb"
 
-        // The environment variables necessary for unbundled perfetto (unnamed domain sockets).
-        // We need unnamed sockets here because SELinux dictates that we cannot use real, file
-        // based, domain sockets on Platform versions prior to S.
-        private const val UNBUNDLED_ENV_PREFIX =
-            "PERFETTO_PRODUCER_SOCK_NAME=@macrobenchmark_producer " +
-                "PERFETTO_CONSUMER_SOCK_NAME=@macrobenchmark_consumer"
-
         // A set of supported ABIs
         private val SUPPORTED_64_ABIS = setOf("arm64-v8a", "x86_64")
         private val SUPPORTED_32_ABIS = setOf("armeabi")
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
index 32bdfaa..4cf3f2b 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.benchmark.macro.perfetto
 
-import android.graphics.Bitmap
 import androidx.benchmark.macro.FileLinkingRule
 import androidx.benchmark.macro.Packages
 import androidx.benchmark.perfetto.PerfettoCapture
@@ -37,7 +36,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import java.io.File
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
 
@@ -47,7 +45,7 @@
  * Note: this test is defined in benchmark-macro instead of benchmark-common so that it can
  * validate trace contents with PerfettoTraceProcessor
  */
-@SdkSuppress(minSdkVersion = 28) // Lowering blocked by b/131359446
+@SdkSuppress(minSdkVersion = 23)
 @RunWith(AndroidJUnit4::class)
 class PerfettoCaptureTest {
     @get:Rule
@@ -60,7 +58,7 @@
     }
 
     @SdkSuppress(
-        minSdkVersion = 28, // must redeclare, or minSdkVersion from this annotation (unset) wins
+        minSdkVersion = 21,
         maxSdkVersion = LOWEST_BUNDLED_VERSION_SUPPORTED - 1
     )
     @SmallTest
@@ -82,18 +80,6 @@
     @Test
     fun captureAndValidateTrace_unbundled() = captureAndValidateTrace(unbundled = true)
 
-    /** Trigger tracing that doesn't require app tracing tag enabled */
-    private fun triggerBitmapTraceSection() {
-        val outFile = File.createTempFile("tempJpg", "jpg")
-        try {
-            Bitmap
-                .createBitmap(100, 100, Bitmap.Config.ARGB_8888)
-                .compress(Bitmap.CompressFormat.JPEG, 100, outFile.outputStream())
-        } finally {
-            outFile.delete()
-        }
-    }
-
     private fun captureAndValidateTrace(unbundled: Boolean) {
         assumeTrue(isAbiSupported())
 
@@ -109,36 +95,33 @@
         // TODO: figure out why this sleep (200ms+) is needed - possibly related to b/194105203
         Thread.sleep(500)
 
-        triggerBitmapTraceSection()
-        trace(CUSTOM_TRACE_SECTION_LABEL) {
-            // Tracing non-trivial duration for manual debugging/verification
-            Thread.sleep(20)
-        }
+        // Tracing non-trivial duration for manual debugging/verification
+        trace(CUSTOM_TRACE_SECTION_LABEL_1) { Thread.sleep(20) }
+        trace(CUSTOM_TRACE_SECTION_LABEL_2) { Thread.sleep(20) }
 
         perfettoCapture.stop(traceFilePath)
 
         val matchingSlices = PerfettoTraceProcessor.querySlices(
             absoluteTracePath = traceFilePath,
-            CUSTOM_TRACE_SECTION_LABEL,
-            BITMAP_TRACE_SECTION_LABEL
+            CUSTOM_TRACE_SECTION_LABEL_1,
+            CUSTOM_TRACE_SECTION_LABEL_2
         )
 
-        // We trigger and verify both bitmap trace section (res-tag), and then custom trace
-        // section (app-tag) which makes it easier to identify when app-tag-specific issues arise
+        // Note: this test avoids validating platform-triggered trace sections, to avoid flakes
+        // from legitimate (and coincidental) platform use during test.
         assertEquals(
-            listOf(BITMAP_TRACE_SECTION_LABEL, CUSTOM_TRACE_SECTION_LABEL),
+            listOf(CUSTOM_TRACE_SECTION_LABEL_1, CUSTOM_TRACE_SECTION_LABEL_2),
             matchingSlices.sortedBy { it.ts }.map { it.name }
         )
         matchingSlices
-            .single { it.name == CUSTOM_TRACE_SECTION_LABEL }
-            .apply {
-                assertTrue(dur > 15_000_000) // should be at least 15ms
+            .forEach {
+                assertTrue(it.dur > 15_000_000) // should be at least 15ms
             }
     }
 
     companion object {
-        const val CUSTOM_TRACE_SECTION_LABEL = "PerfettoCaptureTest"
-        const val BITMAP_TRACE_SECTION_LABEL = "Bitmap.compress"
+        const val CUSTOM_TRACE_SECTION_LABEL_1 = "PerfettoCaptureTest_1"
+        const val CUSTOM_TRACE_SECTION_LABEL_2 = "PerfettoCaptureTest_2"
     }
 }