Add metric for Android blocking calls during CUJ

The metric aggregates a set of blocking calls from processes main thread, scoped for each CUJ.

Bug: 255974834
Test: tools/diff_test_trace_processor.py out/linux_clang_release/trace_processor_shell --name-filter Graphics:android_blocking_calls_cuj --keep-input
Change-Id: I53a565b327367f83b477086185fb8f94b5b88efc
diff --git a/Android.bp b/Android.bp
index 4d69c9a..a4ae406 100644
--- a/Android.bp
+++ b/Android.bp
@@ -4250,6 +4250,7 @@
 genrule {
     name: "perfetto_protos_perfetto_metrics_chrome_descriptor",
     srcs: [
+        "protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto",
         "protos/perfetto/metrics/android/android_frame_timeline_metric.proto",
         "protos/perfetto/metrics/android/android_trusty_workqueues.proto",
         "protos/perfetto/metrics/android/batt_metric.proto",
@@ -4321,6 +4322,7 @@
 genrule {
     name: "perfetto_protos_perfetto_metrics_descriptor",
     srcs: [
+        "protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto",
         "protos/perfetto/metrics/android/android_frame_timeline_metric.proto",
         "protos/perfetto/metrics/android/android_trusty_workqueues.proto",
         "protos/perfetto/metrics/android/batt_metric.proto",
@@ -9693,6 +9695,7 @@
     srcs: [
         "src/trace_processor/metrics/sql/android/android_batt.sql",
         "src/trace_processor/metrics/sql/android/android_binder.sql",
+        "src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql",
         "src/trace_processor/metrics/sql/android/android_camera.sql",
         "src/trace_processor/metrics/sql/android/android_camera_unagg.sql",
         "src/trace_processor/metrics/sql/android/android_cpu.sql",
@@ -9989,6 +9992,7 @@
         "src/trace_processor/stdlib/android/battery.sql",
         "src/trace_processor/stdlib/android/binder.sql",
         "src/trace_processor/stdlib/android/process_metadata.sql",
+        "src/trace_processor/stdlib/android/slices.sql",
         "src/trace_processor/stdlib/android/startup/internal_startups_maxsdk28.sql",
         "src/trace_processor/stdlib/android/startup/internal_startups_minsdk29.sql",
         "src/trace_processor/stdlib/android/startup/internal_startups_minsdk33.sql",
diff --git a/BUILD b/BUILD
index 49778a3..916fcd8 100644
--- a/BUILD
+++ b/BUILD
@@ -1524,6 +1524,7 @@
     srcs = [
         "src/trace_processor/metrics/sql/android/android_batt.sql",
         "src/trace_processor/metrics/sql/android/android_binder.sql",
+        "src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql",
         "src/trace_processor/metrics/sql/android/android_camera.sql",
         "src/trace_processor/metrics/sql/android/android_camera_unagg.sql",
         "src/trace_processor/metrics/sql/android/android_cpu.sql",
@@ -1882,6 +1883,7 @@
         "src/trace_processor/stdlib/android/battery.sql",
         "src/trace_processor/stdlib/android/binder.sql",
         "src/trace_processor/stdlib/android/process_metadata.sql",
+        "src/trace_processor/stdlib/android/slices.sql",
     ],
 )
 
@@ -3495,6 +3497,7 @@
 perfetto_proto_library(
     name = "protos_perfetto_metrics_android_protos",
     srcs = [
+        "protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto",
         "protos/perfetto/metrics/android/android_frame_timeline_metric.proto",
         "protos/perfetto/metrics/android/android_trusty_workqueues.proto",
         "protos/perfetto/metrics/android/batt_metric.proto",
diff --git a/protos/perfetto/metrics/android/BUILD.gn b/protos/perfetto/metrics/android/BUILD.gn
index 4fef3c0..cdce406 100644
--- a/protos/perfetto/metrics/android/BUILD.gn
+++ b/protos/perfetto/metrics/android/BUILD.gn
@@ -20,6 +20,7 @@
     "source_set",
   ]
   sources = [
+    "android_blocking_calls_cuj_metric.proto",
     "android_frame_timeline_metric.proto",
     "android_trusty_workqueues.proto",
     "batt_metric.proto",
diff --git a/protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto b/protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto
new file mode 100644
index 0000000..86a720b
--- /dev/null
+++ b/protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+import "protos/perfetto/metrics/android/process_metadata.proto";
+
+// Blocking calls inside Android CUJs. Shows count and total duration for each.
+message AndroidBlockingCallsCujMetric {
+  repeated Cuj cuj = 1;
+
+  // Next id: 7
+  message Cuj {
+    // ID of the CUJ that is unique within the trace.
+    optional int32 id = 1;
+
+    // Name of the CUJ, extracted from the CUJ jank or latency trace marker.
+    // For example SHADE_EXPAND_COLLAPSE from J<SHADE_EXPAND_COLLAPSE>.
+    optional string name = 2;
+
+    // Details about the process (uid, version, etc)
+    optional AndroidProcessMetadata process = 3;
+
+    // ts of the CUJ trace marker slice, in ns.
+    optional int64 ts = 4;
+
+    // dur of the CUJ trace marker slice, in ns.
+    optional int64 dur = 5;
+
+    // List of blocking calls on the process main thread.
+    repeated BlockingCall blocking_calls = 6;
+  }
+
+  // Blocking call on the main thread.
+  message BlockingCall {
+    // Name of the blocking call
+    optional string name = 1;
+    // Number of times it happened within the CUJ
+    optional int64 cnt = 2;
+    // Total duration within the CUJ
+    optional int64 total_dur_ms = 3;
+  }
+}
diff --git a/protos/perfetto/metrics/metrics.proto b/protos/perfetto/metrics/metrics.proto
index db94258..ede0e0a 100644
--- a/protos/perfetto/metrics/metrics.proto
+++ b/protos/perfetto/metrics/metrics.proto
@@ -20,6 +20,7 @@
 
 import "protos/perfetto/metrics/android/android_frame_timeline_metric.proto";
 import "protos/perfetto/metrics/android/batt_metric.proto";
+import "protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto";
 import "protos/perfetto/metrics/android/cpu_metric.proto";
 import "protos/perfetto/metrics/android/camera_metric.proto";
 import "protos/perfetto/metrics/android/camera_unagg_metric.proto";
@@ -100,7 +101,7 @@
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 49
+// Next id: 50
 message TraceMetrics {
   reserved 4, 10, 13, 14, 16, 19;
 
@@ -232,6 +233,9 @@
   // Metrics for app deadline missed.
   optional AndroidFrameTimelineMetric android_frame_timeline_metric = 47;
 
+  // Blocking calls (e.g. binder calls) for CUJs (important UI transitions).
+  optional AndroidBlockingCallsCujMetric android_blocking_calls_cuj_metric = 49;
+
   // Demo extensions.
   extensions 450 to 499;
 
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index b4956f8..cff1e81 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -47,6 +47,47 @@
 
 // End of protos/perfetto/metrics/android/process_metadata.proto
 
+// Begin of protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto
+
+// Blocking calls inside Android CUJs. Shows count and total duration for each.
+message AndroidBlockingCallsCujMetric {
+  repeated Cuj cuj = 1;
+
+  // Next id: 7
+  message Cuj {
+    // ID of the CUJ that is unique within the trace.
+    optional int32 id = 1;
+
+    // Name of the CUJ, extracted from the CUJ jank or latency trace marker.
+    // For example SHADE_EXPAND_COLLAPSE from J<SHADE_EXPAND_COLLAPSE>.
+    optional string name = 2;
+
+    // Details about the process (uid, version, etc)
+    optional AndroidProcessMetadata process = 3;
+
+    // ts of the CUJ trace marker slice, in ns.
+    optional int64 ts = 4;
+
+    // dur of the CUJ trace marker slice, in ns.
+    optional int64 dur = 5;
+
+    // List of blocking calls on the process main thread.
+    repeated BlockingCall blocking_calls = 6;
+  }
+
+  // Blocking call on the main thread.
+  message BlockingCall {
+    // Name of the blocking call
+    optional string name = 1;
+    // Number of times it happened within the CUJ
+    optional int64 cnt = 2;
+    // Total duration within the CUJ
+    optional int64 total_dur_ms = 3;
+  }
+}
+
+// End of protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto
+
 // Begin of protos/perfetto/metrics/android/android_frame_timeline_metric.proto
 
 message AndroidFrameTimelineMetric {
@@ -1806,7 +1847,7 @@
 
 // Root message for all Perfetto-based metrics.
 //
-// Next id: 49
+// Next id: 50
 message TraceMetrics {
   reserved 4, 10, 13, 14, 16, 19;
 
@@ -1938,6 +1979,9 @@
   // Metrics for app deadline missed.
   optional AndroidFrameTimelineMetric android_frame_timeline_metric = 47;
 
+  // Blocking calls (e.g. binder calls) for CUJs (important UI transitions).
+  optional AndroidBlockingCallsCujMetric android_blocking_calls_cuj_metric = 49;
+
   // Demo extensions.
   extensions 450 to 499;
 
diff --git a/python/perfetto/experimental/slice_breakdown/breakdown.py b/python/perfetto/experimental/slice_breakdown/breakdown.py
index 13dd665..cd1404f 100644
--- a/python/perfetto/experimental/slice_breakdown/breakdown.py
+++ b/python/perfetto/experimental/slice_breakdown/breakdown.py
@@ -48,24 +48,13 @@
   """)
 
   tp.query("""
+    SELECT IMPORT('android.slices');
     CREATE VIEW modded_names AS
     SELECT
       slice.id,
       slice.depth,
       slice.stack_id,
-      CASE
-        WHEN slice.name LIKE 'Choreographer#doFrame%'
-          THEN 'Choreographer#doFrame'
-        WHEN slice.name LIKE 'DrawFrames%'
-          THEN 'DrawFrames'
-        WHEN slice.name LIKE '/data/app%.apk'
-          THEN 'APK load'
-        WHEN slice.name LIKE 'OpenDexFilesFromOat%'
-          THEN 'OpenDexFilesFromOat'
-        WHEN slice.name LIKE 'Open oat file%'
-          THEN 'Open oat file'
-        ELSE slice.name
-      END AS modded_name
+      ANDROID_STANDARDIZE_SLICE_NAME(slice.name) AS modded_name
     FROM slice
   """)
 
diff --git a/python/perfetto/trace_processor/metrics.descriptor b/python/perfetto/trace_processor/metrics.descriptor
index ffc882c..9b3d2e7 100644
--- a/python/perfetto/trace_processor/metrics.descriptor
+++ b/python/perfetto/trace_processor/metrics.descriptor
Binary files differ
diff --git a/python/perfetto/trace_processor/metrics.descriptor.sha1 b/python/perfetto/trace_processor/metrics.descriptor.sha1
index aff5d76..3d2e0ce 100644
--- a/python/perfetto/trace_processor/metrics.descriptor.sha1
+++ b/python/perfetto/trace_processor/metrics.descriptor.sha1
@@ -2,5 +2,5 @@
 // SHA1(tools/gen_binary_descriptors)
 // 6886b319e65925c037179e71a803b8473d06dc7d
 // SHA1(protos/perfetto/metrics/metrics.proto)
-// 493e9c04a68437008b69b5b78cae53802b33c624
+// 5bc40b3771af22a3e705d03fa050f582935480b8
   
\ No newline at end of file
diff --git a/src/trace_processor/metrics/sql/android/BUILD.gn b/src/trace_processor/metrics/sql/android/BUILD.gn
index aff43fd..5fe8985 100644
--- a/src/trace_processor/metrics/sql/android/BUILD.gn
+++ b/src/trace_processor/metrics/sql/android/BUILD.gn
@@ -21,6 +21,7 @@
   sources = [
     "android_batt.sql",
     "android_binder.sql",
+    "android_blocking_calls_cuj_metric.sql",
     "android_camera.sql",
     "android_camera_unagg.sql",
     "android_cpu.sql",
diff --git a/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql b/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql
new file mode 100644
index 0000000..a0aba28
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql
@@ -0,0 +1,181 @@
+--
+-- 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
+--
+--     https://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.
+
+-- Create the base table (`android_jank_cuj`) containing all completed CUJs
+-- found in the trace.
+-- This script will use the `android_jank_cuj_main_thread_cuj_boundary`,
+-- containing bounds of jank CUJs.
+SELECT RUN_METRIC('android/android_jank_cuj.sql');
+
+SELECT IMPORT('android.slices');
+
+-- Jank "J<*>" and latency "L<*>" cujs are put together in android_cujs table.
+-- They are computed separately as latency ones are slightly different, don't
+-- currently have the same way to be cancelled, and are not anchored to vsyncs.
+DROP TABLE IF EXISTS android_cujs;
+CREATE TABLE android_cujs AS
+WITH latency_cujs AS (
+    SELECT
+        ROW_NUMBER() OVER (ORDER BY ts) AS cuj_id,
+        process.upid AS upid,
+        process.name AS process_name,
+        process_metadata.metadata AS process_metadata,
+        -- Extracts "CUJ_NAME" from "L<CUJ_NAME>"
+        SUBSTR(slice.name, 3, LENGTH(slice.name) - 3) AS cuj_name,
+        ts,
+        dur,
+        ts + dur AS ts_end,
+        'completed' AS state
+    FROM slice
+        JOIN process_track
+          ON slice.track_id = process_track.id
+        JOIN process USING (upid)
+        JOIN process_metadata USING (upid)
+    WHERE
+        slice.name GLOB 'L<*>'
+    AND dur > 0
+),
+all_cujs AS (
+    SELECT
+        cuj_id,
+        upid,
+        process_name,
+        process_metadata,
+        cuj_name,
+        tb.ts,
+        tb.dur,
+        tb.ts_end
+    FROM android_jank_cuj_main_thread_cuj_boundary tb
+        JOIN android_jank_cuj using (cuj_id)
+UNION
+    SELECT
+        cuj_id,
+        upid,
+        process_name,
+        process_metadata,
+        cuj_name,
+        ts,
+        dur,
+        ts_end
+    FROM latency_cujs
+)
+SELECT ROW_NUMBER() OVER (ORDER BY ts) AS cuj_id, *
+FROM all_cujs;
+
+
+DROP TABLE IF EXISTS android_blocking_calls_cuj_calls;
+CREATE TABLE android_blocking_calls_cuj_calls AS
+WITH all_main_thread_relevant_slices AS (
+    SELECT DISTINCT
+        ANDROID_STANDARDIZE_SLICE_NAME(s.name) AS name,
+        s.ts,
+        s.track_id,
+        s.dur,
+        s.id,
+        process.name AS process_name,
+        thread.utid,
+        process.upid
+    FROM slice s
+        JOIN thread_track ON s.track_id = thread_track.id
+        JOIN thread USING (utid)
+        JOIN process USING (upid)
+        JOIN android_cujs USING (upid) -- Keeps only slices in cuj processes.
+    WHERE
+        thread.is_main_thread AND (
+               s.name = 'measure'
+            OR s.name = 'layout'
+            OR s.name = 'configChanged'
+            OR s.name = 'Contending for pthread mutex'
+            OR s.name GLOB 'monitor contention with*'
+            OR s.name GLOB 'SuspendThreadByThreadId*'
+            OR s.name GLOB 'LoadApkAssetsFd*'
+            OR s.name GLOB '*binder transaction*'
+            OR s.name GLOB 'inflate*'
+            OR s.name GLOB 'Lock contention on*'
+            OR s.name GLOB '*CancellableContinuationImpl*'
+            OR s.name GLOB 'relayoutWindow*'
+            OR s.name GLOB 'ImageDecoder#decode*'
+        )
+),
+-- Now we have:
+--  (1) a list of slices from the main thread of each process
+--  (2) a list of android cuj with beginning, end, and process
+-- It's needed to:
+--  (1) assign a cuj to each slice. If there are multiple cujs going on during a
+--      slice, there needs to be 2 entries for that slice, one for each cuj id.
+--  (2) each slice needs to be trimmed to be fully inside the cuj associated
+--      (as we don't care about what's outside cujs)
+main_thread_slices_scoped_to_cujs AS (
+SELECT
+    s.id,
+    s.id AS slice_id,
+    s.track_id,
+    s.name,
+    max(s.ts, cuj.ts) AS ts,
+    min(s.ts + s.dur, cuj.ts_end) as ts_end,
+    min(s.ts + s.dur, cuj.ts_end) - max(s.ts, cuj.ts) AS dur,
+    cuj.cuj_id,
+    cuj.cuj_name,
+    s.process_name,
+    s.upid,
+    s.utid
+FROM all_main_thread_relevant_slices s
+    JOIN  android_cujs cuj
+    -- only when there is an overlap
+    ON s.ts + s.dur > cuj.ts AND s.ts < cuj.ts_end
+        -- and are from the same process
+        AND s.upid = cuj.upid
+)
+SELECT
+    name,
+    COUNT(*) AS occurrences,
+    CAST(SUM(dur) / 1e6 AS INT) AS total_dur_ms,
+    upid,
+    cuj_id,
+    cuj_name,
+    process_name
+FROM
+    main_thread_slices_scoped_to_cujs
+GROUP BY name, upid, cuj_id, cuj_name, process_name
+ORDER BY cuj_id;
+
+
+DROP VIEW IF EXISTS android_blocking_calls_cuj_metric_output;
+CREATE VIEW android_blocking_calls_cuj_metric_output AS
+SELECT AndroidBlockingCallsCujMetric('cuj', (
+    SELECT RepeatedField(
+        AndroidBlockingCallsCujMetric_Cuj(
+            'id', cuj_id,
+            'name', cuj_name,
+            'process', process_metadata,
+            'ts',  cuj.ts,
+            'dur', cuj.dur,
+            'blocking_calls', (
+                SELECT RepeatedField(
+                     AndroidBlockingCallsCujMetric_BlockingCall(
+                        'name', b.name,
+                        'cnt', b.occurrences,
+                        'total_dur_ms', b.total_dur_ms
+                    )
+                )
+                FROM android_blocking_calls_cuj_calls b
+                WHERE b.cuj_id = cuj.cuj_id and b.upid = cuj.upid
+                ORDER BY total_dur_ms DESC
+            )
+        )
+    )
+    FROM android_cujs cuj
+    ORDER BY cuj.cuj_id ASC
+));
diff --git a/src/trace_processor/stdlib/android/BUILD.gn b/src/trace_processor/stdlib/android/BUILD.gn
index ea6659e..844e4d7 100644
--- a/src/trace_processor/stdlib/android/BUILD.gn
+++ b/src/trace_processor/stdlib/android/BUILD.gn
@@ -20,5 +20,6 @@
     "battery.sql",
     "binder.sql",
     "process_metadata.sql",
+    "slices.sql",
   ]
 }
diff --git a/src/trace_processor/stdlib/android/slices.sql b/src/trace_processor/stdlib/android/slices.sql
new file mode 100644
index 0000000..7cd80e7
--- /dev/null
+++ b/src/trace_processor/stdlib/android/slices.sql
@@ -0,0 +1,45 @@
+--
+-- 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
+--
+--     https://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.
+
+-- Some slice names have params in them. This functions removes them to make it
+-- possible to aggregate by name.
+-- Some examples are:
+--  - Lock/monitor contention slices. The name includes where the lock
+--    contention is in the code. That part is removed.
+--  - DrawFrames/ooFrame. The name also includes the frame number.
+--  - Apk/oat/dex loading: The name of the apk is removed
+--
+-- @arg name STRING   Raw name of the slice
+-- @ret STRING        Simplified name.
+SELECT CREATE_FUNCTION(
+    'ANDROID_STANDARDIZE_SLICE_NAME(name STRING)',
+    'STRING',
+    '
+    SELECT
+        CASE
+        WHEN $name GLOB "Lock contention on*" THEN "Lock contention on <...>"
+        WHEN $name GLOB "monitor contention with*" THEN "monitor contention with <...>"
+        WHEN $name GLOB "SuspendThreadByThreadId*" THEN "SuspendThreadByThreadId <...>"
+        WHEN $name GLOB "LoadApkAssetsFd*" THEN "LoadApkAssetsFd <...>"
+        WHEN $name GLOB "relayoutWindow*" THEN "relayoutWindow <...>"
+        WHEN $name GLOB "*CancellableContinuationImpl*" THEN "CoroutineContinuation"
+        WHEN $name GLOB "Choreographer#doFrame*" THEN "Choreographer#doFrame"
+        WHEN $name GLOB "DrawFrames*" THEN "DrawFrames"
+        WHEN $name GLOB "/data/app*.apk" THEN "APK load"
+        WHEN $name GLOB "OpenDexFilesFromOat*" THEN "OpenDexFilesFromOat"
+        WHEN $name GLOB "Open oat file*" THEN "Open oat file"
+        ELSE $name
+        END
+');
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out
new file mode 100644
index 0000000..81e1700
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out
@@ -0,0 +1,142 @@
+android_blocking_calls_cuj_metric {
+  cuj {
+    id: 1
+    name: "TEST_SYSUI_LATENCY_EVENT"
+    process {
+      name: "com.android.systemui"
+      uid: 10001
+    }
+    ts: 2000000
+    dur: 15000000
+    blocking_calls {
+      name: "binder transaction"
+      cnt: 4
+      total_dur_ms: 6
+    }
+  }
+  cuj {
+    id: 2
+    name: "TEST_LAUNCHER_LATENCY_EVENT"
+    process {
+      name: "com.google.android.apps.nexuslauncher"
+      uid: 10002
+    }
+    ts: 2000000
+    dur: 15000000
+    blocking_calls {
+      name: "binder transaction"
+      cnt: 4
+      total_dur_ms: 6
+    }
+  }
+  cuj {
+    id: 3
+    name: "CUJ_WITH_MANY_BLOCKING_CALLS"
+    process {
+      name: "com.google.android.third.process"
+      uid: 10003
+    }
+    ts: 2000000
+    dur: 150000000
+    blocking_calls {
+      name: "Contending for pthread mutex"
+      cnt: 1
+      total_dur_ms: 10
+    }
+    blocking_calls {
+      name: "CoroutineContinuation"
+      cnt: 1
+      total_dur_ms: 10
+    }
+    blocking_calls {
+      name: "ImageDecoder#decodeBitmap"
+      cnt: 1
+      total_dur_ms: 10
+    }
+    blocking_calls {
+      name: "ImageDecoder#decodeDrawable"
+      cnt: 1
+      total_dur_ms: 10
+    }
+    blocking_calls {
+      name: "LoadApkAssetsFd <...>"
+      cnt: 1
+      total_dur_ms: 10
+    }
+    blocking_calls {
+      name: "Lock contention on <...>"
+      cnt: 1
+      total_dur_ms: 10
+    }
+    blocking_calls {
+      name: "SuspendThreadByThreadId <...>"
+      cnt: 1
+      total_dur_ms: 10
+    }
+    blocking_calls {
+      name: "binder transaction"
+      cnt: 1
+      total_dur_ms: 10
+    }
+    blocking_calls {
+      name: "configChanged"
+      cnt: 1
+      total_dur_ms: 10
+    }
+    blocking_calls {
+      name: "inflate"
+      cnt: 1
+      total_dur_ms: 10
+    }
+    blocking_calls {
+      name: "layout"
+      cnt: 1
+      total_dur_ms: 10
+    }
+    blocking_calls {
+      name: "measure"
+      cnt: 1
+      total_dur_ms: 10
+    }
+    blocking_calls {
+      name: "monitor contention with <...>"
+      cnt: 1
+      total_dur_ms: 10
+    }
+    blocking_calls {
+      name: "relayoutWindow <...>"
+      cnt: 1
+      total_dur_ms: 10
+    }
+  }
+  cuj {
+    id: 4
+    name: "OVERLAPPING_CUJ_1"
+    process {
+      name: "com.android.systemui"
+      uid: 10001
+    }
+    ts: 20000000
+    dur: 10000000
+    blocking_calls {
+      name: "monitor contention with <...>"
+      cnt: 1
+      total_dur_ms: 10
+    }
+  }
+  cuj {
+    id: 5
+    name: "OVERLAPPING_CUJ_2"
+    process {
+      name: "com.android.systemui"
+      uid: 10001
+    }
+    ts: 22000000
+    dur: 10000000
+    blocking_calls {
+      name: "monitor contention with <...>"
+      cnt: 1
+      total_dur_ms: 10
+    }
+  }
+}
diff --git a/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py
new file mode 100755
index 0000000..7573b76
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py
@@ -0,0 +1,185 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from os import sys, path
+import synth_common
+
+# com.android.systemui
+SYSUI_PID = 1000
+# com.google.android.apps.nexuslauncher
+LAUNCHER_PID = 2000
+
+THIRD_PROCESS_PID = 3000
+
+# List of blocking calls
+blocking_call_names = [
+    'monitor contention with something else', 'SuspendThreadByThreadId 123',
+    'LoadApkAssetsFd 123', 'binder transaction',
+    'inflate', 'Lock contention on thread list lock (owner tid: 1665)',
+    'CancellableContinuationImpl#123', 'relayoutWindow*', 'measure', 'layout',
+    'configChanged', 'Contending for pthread mutex',
+    'ImageDecoder#decodeBitmap', 'ImageDecoder#decodeDrawable',
+    'Should not be in the metric'
+]
+
+
+def add_main_thread_atrace(trace, ts, ts_end, buf, pid):
+  trace.add_atrace_begin(ts=ts, tid=pid, pid=pid, buf=buf)
+  trace.add_atrace_end(ts=ts_end, tid=pid, pid=pid)
+
+
+def add_async_trace(trace, ts, ts_end, buf, pid):
+  trace.add_atrace_async_begin(ts=ts, tid=pid, pid=pid, buf=buf)
+  trace.add_atrace_async_end(ts=ts_end, tid=pid, pid=pid, buf=buf)
+
+
+# Adds a set of predefined blocking calls in places near the cuj boundaries to
+# verify that only the portion inside the cuj is counted in the metric.
+def add_cuj_with_blocking_calls(trace, cuj_name, pid):
+  cuj_begin = 2_000_000
+  cuj_dur = 15_000_000
+  cuj_end = cuj_begin + cuj_dur
+  blocking_call_name = "binder transaction"
+
+  add_async_trace(trace, ts=cuj_begin, ts_end=cuj_end, buf=cuj_name, pid=pid)
+
+  # all outside, before cuj, shouldn't be counted
+  add_main_thread_atrace(
+      trace,
+      ts=cuj_begin - 2_000_000,
+      ts_end=cuj_begin - 1_000_000,
+      buf=blocking_call_name,
+      pid=pid)
+
+  # mid inside, mid outside. Should account for half the time.
+  add_main_thread_atrace(
+      trace,
+      ts=cuj_begin - 1_000_000,
+      ts_end=cuj_begin + 1_000_000,
+      buf=blocking_call_name,
+      pid=pid)
+
+  # completely inside
+  add_main_thread_atrace(
+      trace,
+      ts=cuj_begin + 2_000_000,
+      ts_end=cuj_begin + 3_000_000,
+      buf=blocking_call_name,
+      pid=pid)
+
+  add_main_thread_atrace(
+      trace,
+      ts=cuj_begin + 4_000_000,
+      ts_end=cuj_begin + 7_000_000,
+      buf=blocking_call_name,
+      pid=pid)
+
+  # mid inside, mid outside
+  add_main_thread_atrace(
+      trace,
+      ts=cuj_end - 1_000_000,
+      ts_end=cuj_end + 1_000_000,
+      buf=blocking_call_name,
+      pid=pid)
+
+  # all outside, after cuj, shouldn't be counted/
+  add_main_thread_atrace(
+      trace,
+      ts=cuj_end + 2_000_000,
+      ts_end=cuj_end + 3_000_000,
+      buf=blocking_call_name,
+      pid=pid)
+
+
+# Creates a cuj that contains one of each blocking call.
+def add_all_blocking_calls_in_cuj(trace, pid):
+  blocking_call_dur = 10_000_000
+  blocking_call_ts = 2_000_000
+
+  cuj_dur = len(blocking_call_names) * blocking_call_dur
+  add_async_trace(
+      trace,
+      ts=blocking_call_ts,
+      ts_end=blocking_call_ts + cuj_dur,
+      buf="L<CUJ_WITH_MANY_BLOCKING_CALLS>",
+      pid=pid)
+
+  for blocking_call in blocking_call_names:
+    add_main_thread_atrace(
+        trace,
+        ts=blocking_call_ts,
+        ts_end=blocking_call_ts + blocking_call_dur,
+        buf=blocking_call,
+        pid=pid)
+    blocking_call_ts += blocking_call_dur
+
+# Creates 2 overlapping cuj, and a blocking call that lasts for both of them.
+def add_overlapping_cujs_with_blocking_calls(trace, start_ts, pid):
+  add_async_trace(
+      trace,
+      ts=start_ts,
+      ts_end=start_ts + 10_000_000,
+      buf="L<OVERLAPPING_CUJ_1>",
+      pid=pid)
+  add_async_trace(
+      trace,
+      ts=start_ts + 2_000_000,
+      ts_end=start_ts + 12_000_000,
+      buf="L<OVERLAPPING_CUJ_2>",
+      pid=pid)
+
+  add_main_thread_atrace(
+      trace,
+      ts=start_ts,
+      ts_end=start_ts + 12_000_000,
+      buf=blocking_call_names[0],
+      pid=pid)
+
+
+def add_process(trace, package_name, uid, pid):
+  trace.add_package_list(ts=0, name=package_name, uid=uid, version_code=1)
+  trace.add_process(
+      pid=pid, ppid=0, cmdline=package_name, uid=uid)
+  trace.add_thread(tid=pid, tgid=pid, cmdline="MainThread", name="MainThread")
+
+
+def setup_trace():
+  trace = synth_common.create_trace()
+  trace.add_packet()
+  add_process(trace, package_name="com.android.systemui", uid=10001,
+              pid=SYSUI_PID)
+  add_process(trace, package_name="com.google.android.apps.nexuslauncher",
+              uid=10002, pid=LAUNCHER_PID)
+  add_process(trace, package_name="com.google.android.third.process",
+              uid=10003, pid=THIRD_PROCESS_PID)
+  trace.add_ftrace_packet(cpu=0)
+  add_async_trace(trace, ts=0, ts_end=5, buf="J<IGNORED>", pid=SYSUI_PID)
+  return trace
+
+
+trace = setup_trace()
+
+add_cuj_with_blocking_calls(trace, "L<TEST_SYSUI_LATENCY_EVENT>", pid=SYSUI_PID)
+add_cuj_with_blocking_calls(trace, "L<TEST_LAUNCHER_LATENCY_EVENT>",
+                            pid=LAUNCHER_PID)
+
+add_all_blocking_calls_in_cuj(trace, pid=THIRD_PROCESS_PID)
+
+add_overlapping_cujs_with_blocking_calls(trace, pid=SYSUI_PID,
+                                         start_ts=20_000_000)
+
+# Note that J<*> events are not tested here.
+# See test_android_blocking_calls_on_jank_cujs.
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/android/android_blocking_calls_on_jank_cuj_metric.out b/test/trace_processor/diff_tests/android/android_blocking_calls_on_jank_cuj_metric.out
new file mode 100644
index 0000000..babc4da
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_blocking_calls_on_jank_cuj_metric.out
@@ -0,0 +1,52 @@
+android_blocking_calls_cuj_metric {
+  cuj {
+    id: 1
+    name: "SHOULD_BE_IGNORED"
+    process {
+      name: "com.android.systemui"
+      uid: 10001
+      package {
+        package_name: "com.android.systemui"
+        apk_version_code: 1
+        debuggable: false
+      }
+      packages_for_uid {
+        package_name: "com.android.systemui"
+        apk_version_code: 1
+        debuggable: false
+      }
+    }
+    ts: 0
+    dur: 115000000
+    blocking_calls {
+      name: "binder transaction"
+      cnt: 11
+      total_dur_ms: 10
+    }
+  }
+  cuj {
+    id: 2
+    name: "SHADE_ROW_EXPAND"
+    process {
+      name: "com.android.systemui"
+      uid: 10001
+      package {
+        package_name: "com.android.systemui"
+        apk_version_code: 1
+        debuggable: false
+      }
+      packages_for_uid {
+        package_name: "com.android.systemui"
+        apk_version_code: 1
+        debuggable: false
+      }
+    }
+    ts: 0
+    dur: 802000000
+    blocking_calls {
+      name: "binder transaction"
+      cnt: 13
+      total_dur_ms: 68
+    }
+  }
+}
diff --git a/test/trace_processor/diff_tests/android/android_slice_standardization.out b/test/trace_processor/diff_tests/android/android_slice_standardization.out
new file mode 100644
index 0000000..2dd4186
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_slice_standardization.out
@@ -0,0 +1,12 @@
+"name"
+"APK load"
+"Choreographer#doFrame"
+"CoroutineContinuation"
+"DrawFrames"
+"LoadApkAssetsFd <...>"
+"Lock contention on <...>"
+"Open oat file"
+"OpenDexFilesFromOat"
+"SuspendThreadByThreadId <...>"
+"monitor contention with <...>"
+"relayoutWindow <...>"
diff --git a/test/trace_processor/diff_tests/android/android_slice_standardization.py b/test/trace_processor/diff_tests/android/android_slice_standardization.py
new file mode 100644
index 0000000..a0556df
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_slice_standardization.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from os import sys, path
+import synth_common
+
+SYSUI_PID = 1000
+
+trace = synth_common.create_trace()
+trace.add_packet()
+
+trace.add_package_list(ts=0, name="com.android.systemui", uid=SYSUI_PID,
+                       version_code=1)
+trace.add_process(pid=SYSUI_PID, ppid=0, cmdline="com.android.systemui",
+                  uid=SYSUI_PID)
+trace.add_ftrace_packet(cpu=0)
+
+
+slices_to_standardize = [
+    "Lock contention on thread list lock (owner tid: 1665)",
+    "monitor contention with owner BG Thread #1 (30) at",
+    "SuspendThreadByThreadId suspended Primes-1 id=19",
+    "LoadApkAssetsFd({ParcelFileDescriptor: java.io.FileDescriptor@340019d})",
+    "relayoutWindow#first=false/resize=false/vis=true/params=true/force=false",
+    "android.os.Handler: kotlinx.coroutines.CancellableContinuationImpl",
+    "Choreographer#doFrame 122932914",
+    "DrawFrames 122921845",
+    "/data/app/.../base.apk",
+    "OpenDexFilesFromOat(/data/app/.../base.apk)",
+    "Open oat file /data/misc/apexdata/com.android.art/dalvik-cache/boot.oat",
+]
+
+for name in slices_to_standardize:
+  trace.add_atrace_async_begin(ts=1_000_000, tid=SYSUI_PID, pid=SYSUI_PID,
+                               buf=name)
+  trace.add_atrace_async_end(ts=2_000_000, tid=SYSUI_PID, pid=SYSUI_PID,
+                             buf=name)
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/android/tests.py b/test/trace_processor/diff_tests/android/tests.py
index 4127021..02c5664 100644
--- a/test/trace_processor/diff_tests/android/tests.py
+++ b/test/trace_processor/diff_tests/android/tests.py
@@ -196,3 +196,26 @@
         trace=DataPath('android_binder_metric_trace.atr'),
         query=Metric('android_binder'),
         out=Path('android_binder_metric.out'))
+
+  def test_android_blocking_calls_cuj(self):
+    return DiffTestBlueprint(
+        trace=Path('android_blocking_calls_cuj_metric.py'),
+        query=Metric('android_blocking_calls_cuj_metric'),
+        out=Path('android_blocking_calls_cuj_metric.out'))
+
+  def test_android_blocking_calls_on_jank_cujs(self):
+    return DiffTestBlueprint(
+        trace=Path('../graphics/android_jank_cuj.py'),
+        query=Metric('android_blocking_calls_cuj_metric'),
+        out=Path('android_blocking_calls_on_jank_cuj_metric.out'))
+
+  def test_android_slices_standardization_for_aggregation(self):
+    return DiffTestBlueprint(
+        trace=Path('android_slice_standardization.py'),
+        query="""
+        SELECT IMPORT('android.slices');
+        SELECT ANDROID_STANDARDIZE_SLICE_NAME(slice.name) name
+        FROM slice
+        ORDER BY name;
+        """,
+        out=Path('android_slice_standardization.out'))