tp: Add support for pre 29 SDK startups.

Change-Id: Ic2b8efc8665e2afbbf8bc71720b58c7a58f59c57
diff --git a/Android.bp b/Android.bp
index 43d2027..40484c4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12380,6 +12380,7 @@
         "src/trace_processor/perfetto_sql/stdlib/android/dvfs.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/frames/per_frame_metrics.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/frames/timeline.sql",
+        "src/trace_processor/perfetto_sql/stdlib/android/frames/timeline_maxsdk28.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/freezer.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/garbage_collection.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/input.sql",
diff --git a/BUILD b/BUILD
index 2b53c16..4e316ed 100644
--- a/BUILD
+++ b/BUILD
@@ -2392,6 +2392,7 @@
     srcs = [
         "src/trace_processor/perfetto_sql/stdlib/android/frames/per_frame_metrics.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/frames/timeline.sql",
+        "src/trace_processor/perfetto_sql/stdlib/android/frames/timeline_maxsdk28.sql",
     ],
 )
 
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/frames/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/android/frames/BUILD.gn
index bc434d5..306758a 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/frames/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/android/frames/BUILD.gn
@@ -18,5 +18,6 @@
   sources = [
     "per_frame_metrics.sql",
     "timeline.sql",
+    "timeline_maxsdk28.sql",
   ]
 }
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/frames/timeline.sql b/src/trace_processor/perfetto_sql/stdlib/android/frames/timeline.sql
index b492bb3..3d29281 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/frames/timeline.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/frames/timeline.sql
@@ -13,6 +13,9 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
+INCLUDE PERFETTO MODULE slices.with_context;
+INCLUDE PERFETTO MODULE android.frames.timeline_maxsdk28;
+
 -- Parses the slice name to fetch `frame_id` from `slice` table.
 -- Use with caution. Slice names are a flaky source of ids and the resulting
 -- table might require some further operations.
@@ -23,13 +26,16 @@
     -- `slice.id` of the frame slice.
     id INT,
     -- Parsed frame id.
-    frame_id INT
+    frame_id INT,
+    -- Utid.
+    utid INT
 ) AS
 WITH all_found AS (
     SELECT
         id,
-        cast_int!(STR_SPLIT(name, ' ', 1)) AS frame_id
-    FROM slice
+        cast_int!(STR_SPLIT(name, ' ', 1)) AS frame_id,
+        utid
+    FROM thread_slice
     WHERE name GLOB $glob_str
 )
 SELECT *
@@ -47,11 +53,10 @@
     ui_thread_utid INT
 ) AS
 SELECT
-    c.*,
-    thread_track.utid AS ui_thread_utid
-FROM _get_frame_table_with_id('Choreographer#doFrame*') c
-JOIN slice USING (id)
-JOIN thread_track ON (thread_track.id = slice.track_id);
+    id,
+    frame_id,
+    utid AS ui_thread_utid
+FROM _get_frame_table_with_id('Choreographer#doFrame*');
 
 -- All of the `DrawFrame` slices with their frame id and render thread.
 -- There might be multiple DrawFrames slices for a single vsync (frame id).
@@ -66,11 +71,10 @@
     render_thread_utid INT
 ) AS
 SELECT
-    d.*,
-    thread_track.utid AS render_thread_utid
-FROM _get_frame_table_with_id('DrawFrame*') d
-JOIN slice USING (id)
-JOIN thread_track ON (thread_track.id = slice.track_id);
+    id,
+    frame_id,
+    utid AS render_thread_utid
+FROM _get_frame_table_with_id('DrawFrame*');
 
 -- `actual_frame_timeline_slice` returns the same slice on different tracks.
 -- We are getting the first slice with one frame id.
@@ -119,21 +123,47 @@
     -- `utid` of the UI thread.
     ui_thread_utid INT
 ) AS
+WITH frames_sdk_after_28 AS (
 SELECT
     frame_id,
     ts,
     dur,
     do_frame.id AS do_frame_id,
     draw_frame.id AS draw_frame_id,
-    act.id AS actual_frame_timeline_id,
-    exp.id AS expected_frame_timeline_id,
     draw_frame.render_thread_utid,
-    do_frame.ui_thread_utid
+    do_frame.ui_thread_utid,
+    "after_28" AS sdk,
+    act.id AS actual_frame_timeline_id,
+    exp.id AS expected_frame_timeline_id
 FROM android_frames_choreographer_do_frame do_frame
 JOIN android_frames_draw_frame draw_frame USING (frame_id)
 JOIN _distinct_from_actual_timeline_slice act USING (frame_id)
 JOIN _distinct_from_expected_timeline_slice exp USING (frame_id)
-ORDER BY frame_id;
+ORDER BY frame_id
+),
+all_frames AS (
+    SELECT * FROM frames_sdk_after_28
+    UNION
+    SELECT
+        *,
+        NULL AS actual_frame_timeline_id,
+        NULL AS expected_frame_timeline_id
+    FROM _frames_maxsdk_28
+)
+SELECT
+    frame_id,
+    ts,
+    dur,
+    do_frame_id,
+    draw_frame_id,
+    actual_frame_timeline_id,
+    expected_frame_timeline_id,
+    render_thread_utid,
+    ui_thread_utid
+FROM all_frames
+WHERE sdk = IIF(
+    (SELECT COUNT(1) FROM actual_frame_timeline_slice) > 0,
+    "after_28", "maxsdk28");
 
 -- Returns first frame after the provided timestamp. The returning table has at
 -- most one row.
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/frames/timeline_maxsdk28.sql b/src/trace_processor/perfetto_sql/stdlib/android/frames/timeline_maxsdk28.sql
new file mode 100644
index 0000000..b8fad07
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/android/frames/timeline_maxsdk28.sql
@@ -0,0 +1,76 @@
+--
+-- Copyright 2024 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.
+
+INCLUDE PERFETTO MODULE slices.with_context;
+
+-- All slices related to one frame for max SDK 28. Aggregates
+-- "Choreographer#doFrame" and "DrawFrame". Tries to guess the `ts` and `dur`
+-- of the frame by first guessing which "DrawFrame" slices are related to which
+-- "Choreographer#doSlice".
+CREATE PERFETTO TABLE _frames_maxsdk_28(
+    -- Frame id. Created manually starting from 0.
+    frame_id INT,
+    -- Timestamp of the frame. Start of "Choreographer#doFrame" slice.
+    ts INT,
+    -- Duration of the frame, defined as the duration until the last
+    -- "DrawFrame" of this frame finishes.
+    dur INT,
+    -- `slice.id` of "Choreographer#doFrame" slice.
+    do_frame_id INT,
+    -- `slice.id` of "DrawFrame" slice. Fetched as one of the "DrawFrame"
+    -- slices that happen for the same process as "Choreographer#doFrame" slice
+    -- and start after it started and before the next "doFrame" started.
+    draw_frame_id INT,
+    -- `utid` of the render thread.
+    render_thread_utid INT,
+    -- `utid` of the UI thread.
+    ui_thread_utid INT,
+    -- "maxsdk28"
+    sdk STRING
+) AS
+WITH do_frames AS (
+    SELECT
+        id,
+        ts,
+        LEAD(ts, 1, TRACE_END()) OVER (PARTITION BY upid ORDER BY ts) AS next_do_frame,
+        utid,
+        upid
+    FROM thread_slice
+    WHERE name = 'Choreographer#doFrame' AND is_main_thread = 1
+    ORDER BY ts
+),
+draw_frames AS (
+    SELECT
+        id,
+        ts,
+        dur,
+        ts + dur AS ts_end,
+        utid,
+        upid
+    FROM thread_slice
+    WHERE name = 'DrawFrame'
+)
+SELECT
+  ROW_NUMBER() OVER () AS frame_id,
+  do.ts,
+  MAX(draw.ts_end) OVER (PARTITION BY do.id) - do.ts AS dur,
+  do.id AS do_frame_id,
+  draw.id AS draw_frame_id,
+  draw.utid AS render_thread_utid,
+  do.utid AS ui_thread_utid,
+  "maxsdk28" AS sdk
+FROM do_frames do
+JOIN draw_frames draw ON (do.upid = draw.upid AND draw.ts >= do.ts AND draw.ts < next_do_frame)
+ORDER BY do.ts;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/startup/startups_maxsdk28.sql b/src/trace_processor/perfetto_sql/stdlib/android/startup/startups_maxsdk28.sql
index 0a7fab2..5188b63 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/startup/startups_maxsdk28.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/startup/startups_maxsdk28.sql
@@ -13,17 +13,51 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
+INCLUDE PERFETTO MODULE slices.with_context;
 INCLUDE PERFETTO MODULE android.startup.startup_events;
+INCLUDE PERFETTO MODULE android.frames.timeline;
 
 CREATE PERFETTO TABLE _startups_maxsdk28 AS
+-- Warm and cold starts only are based on the launching slice
+WITH warm_and_cold AS (
+  SELECT
+    le.ts,
+    le.ts_end AS ts_end,
+    package_name AS package
+  FROM _startup_events le
+),
+-- Hot starts don’t have a launching slice so we use activityResume as a
+-- proxy.
+--
+-- Note that this implementation will also count warm and cold starts but
+-- we will remove those below.
+maybe_hot AS (
+  SELECT
+    sl.ts,
+    rs.ts + rs.dur AS ts_end,
+    -- We use the process name as the package as we have no better option.
+    process_name AS package
+  FROM thread_slice sl
+  JOIN android_first_frame_after(sl.ts) rs
+  WHERE name = 'activityResume'
+  -- Remove any launches here where the activityResume slices happens during
+  -- a warm/cold startup.
+  AND sl.ts NOT IN (SELECT ts FROM warm_and_cold)
+),
+cold_warm_hot AS (
+  SELECT * FROM warm_and_cold
+  UNION ALL
+  SELECT * FROM maybe_hot
+
+)
 SELECT
-  "maxsdk28" as sdk,
+  "maxsdk28" AS sdk,
   ROW_NUMBER() OVER(ORDER BY ts) AS startup_id,
-  le.ts,
-  le.ts_end AS ts_end,
-  le.ts_end - le.ts AS dur,
-  package_name AS package,
+  ts,
+  ts_end,
+  ts_end - ts AS dur,
+  package,
   NULL AS startup_type
-FROM _startup_events le
-ORDER BY ts;
+FROM cold_warm_hot;
+
 
diff --git a/src/trace_processor/perfetto_sql/stdlib/slices/with_context.sql b/src/trace_processor/perfetto_sql/stdlib/slices/with_context.sql
index 6e9ba0d..9430bd9 100644
--- a/src/trace_processor/perfetto_sql/stdlib/slices/with_context.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/slices/with_context.sql
@@ -36,8 +36,10 @@
   thread_name STRING,
   -- Alias for `thread.utid`.
   utid INT,
-  -- Alias for `thread.tid`
+  -- Alias for `thread.tid`.
   tid INT,
+  -- Alias for `thread.is_main_thread`.
+  is_main_thread BOOL,
   -- Alias for `process.name`.
   process_name STRING,
   -- Alias for `process.upid`.
@@ -67,6 +69,7 @@
   thread.name AS thread_name,
   thread.utid,
   thread.tid,
+  thread.is_main_thread,
   process.name AS process_name,
   process.upid,
   process.pid,
diff --git a/test/data/api24_startup_cold.perfetto-trace.sha256 b/test/data/api24_startup_cold.perfetto-trace.sha256
new file mode 100644
index 0000000..dbc4e62
--- /dev/null
+++ b/test/data/api24_startup_cold.perfetto-trace.sha256
@@ -0,0 +1 @@
+ac006a3ec74fac70feb58c2cfad840c552af0ebd25f03bdfbf14d5f77148764b
\ No newline at end of file
diff --git a/test/data/api24_startup_hot.perfetto-trace.sha256 b/test/data/api24_startup_hot.perfetto-trace.sha256
new file mode 100644
index 0000000..6fa48c6
--- /dev/null
+++ b/test/data/api24_startup_hot.perfetto-trace.sha256
@@ -0,0 +1 @@
+0ec527c6392adf16e8580ba0fa3eaccf69eccba36c863377eca5c5618f00f3ea
\ No newline at end of file
diff --git a/test/data/api24_startup_warm.perfetto-trace.sha256 b/test/data/api24_startup_warm.perfetto-trace.sha256
new file mode 100644
index 0000000..04eca80
--- /dev/null
+++ b/test/data/api24_startup_warm.perfetto-trace.sha256
@@ -0,0 +1 @@
+59d1e84ddf6a492c10d7b6ecebf7b80813e6a0a2b3bef7d854d5ef3cf7210137
\ No newline at end of file
diff --git a/test/data/api31_startup_hot.perfetto-trace.sha256 b/test/data/api31_startup_hot.perfetto-trace.sha256
new file mode 100644
index 0000000..8cac597
--- /dev/null
+++ b/test/data/api31_startup_hot.perfetto-trace.sha256
@@ -0,0 +1 @@
+072802c0adf6968d0eb9c56fa13b33cad32f583398d019dc8a798981164333c5
\ No newline at end of file
diff --git a/test/data/api32_startup_warm.perfetto-trace.sha256 b/test/data/api32_startup_warm.perfetto-trace.sha256
new file mode 100644
index 0000000..f83dd34
--- /dev/null
+++ b/test/data/api32_startup_warm.perfetto-trace.sha256
@@ -0,0 +1 @@
+776122b5660c5d6e738950031fdb4992a64e3224e9e82bbaed474a0a281ed7e3
\ No newline at end of file
diff --git a/test/data/api34_startup_cold.perfetto-trace.sha256 b/test/data/api34_startup_cold.perfetto-trace.sha256
new file mode 100644
index 0000000..2a7a044
--- /dev/null
+++ b/test/data/api34_startup_cold.perfetto-trace.sha256
@@ -0,0 +1 @@
+1958521dc5128cd4eadd1df281e19987aded718750e6883f82ffd3b5eb529bd6
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index 1f711b8..cb82c55 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -92,6 +92,7 @@
 from diff_tests.parser.translated_args.tests import TranslatedArgs
 from diff_tests.parser.ufs.tests import Ufs
 from diff_tests.stdlib.android.frames_tests import Frames
+from diff_tests.stdlib.android.startups_tests import Startups
 from diff_tests.stdlib.android.tests import AndroidStdlib
 from diff_tests.stdlib.chrome.chrome_stdlib_testsuites import CHROME_STDLIB_TESTSUITES
 from diff_tests.stdlib.common.tests import StdlibCommon
@@ -282,6 +283,7 @@
                        'StdlibIntervals').fetch(),
       *IntervalsIntersect(index_path, 'stdlib/intervals',
                           'StdlibIntervalsIntersect').fetch(),
+      *Startups(index_path, 'stdlib/android', 'Startups').fetch(),
       *Timestamps(index_path, 'stdlib/timestamps', 'Timestamps').fetch(),
       *WattsonStdlib(index_path, 'stdlib/wattson', 'WattsonStdlib').fetch(),
   ] + chrome_stdlib_tests
diff --git a/test/trace_processor/diff_tests/stdlib/android/startups_tests.py b/test/trace_processor/diff_tests/stdlib/android/startups_tests.py
new file mode 100644
index 0000000..14d002a
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/android/startups_tests.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+# Copyright (C) 2024 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath
+from python.generators.diff_tests.testing import Csv, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Startups(TestSuite):
+
+  def test_hot_startups(self):
+    return DiffTestBlueprint(
+        trace=DataPath('api31_startup_hot.perfetto-trace'),
+        query="""
+        INCLUDE PERFETTO MODULE android.startup.startups;
+        SELECT * FROM android_startups;
+        """,
+        out=Csv("""
+        "startup_id","ts","ts_end","dur","package","startup_type"
+        1,186969441973689,186969489302704,47329015,"androidx.benchmark.integration.macrobenchmark.target","[NULL]"
+        """))
+
+  def test_warm_startups(self):
+    return DiffTestBlueprint(
+        trace=DataPath('api32_startup_warm.perfetto-trace'),
+        query="""
+        INCLUDE PERFETTO MODULE android.startup.startups;
+        SELECT * FROM android_startups;
+        """,
+        out=Csv("""
+        "startup_id","ts","ts_end","dur","package","startup_type"
+        28,157479786566030,157479943081777,156515747,"androidx.benchmark.integration.macrobenchmark.target","[NULL]"
+        """))
+
+  def test_cold_startups(self):
+    return DiffTestBlueprint(
+        trace=DataPath('api34_startup_cold.perfetto-trace'),
+        query="""
+        INCLUDE PERFETTO MODULE android.startup.startups;
+        SELECT * FROM android_startups;
+        """,
+        out=Csv("""
+        "startup_id","ts","ts_end","dur","package","startup_type"
+        61,17806781251694,17806891032171,109780477,"com.android.systemui.people","warm"
+        """))
+
+  def test_hot_startups_maxsdk28(self):
+    return DiffTestBlueprint(
+        trace=DataPath('api24_startup_hot.perfetto-trace'),
+        query="""
+        INCLUDE PERFETTO MODULE android.startup.startups;
+        SELECT * FROM android_startups;
+        """,
+        out=Csv("""
+        "startup_id","ts","ts_end","dur","package","startup_type"
+        1,779860286416,779893485322,33198906,"com.google.android.googlequicksearchbox","[NULL]"
+        2,780778904571,780813944498,35039927,"androidx.benchmark.integration.macrobenchmark.target","[NULL]"
+        """))
+
+  def test_warm_startups_maxsdk28(self):
+    return DiffTestBlueprint(
+        trace=DataPath('api24_startup_warm.perfetto-trace'),
+        query="""
+        INCLUDE PERFETTO MODULE android.startup.startups;
+        SELECT * FROM android_startups;
+        """,
+        out=Csv("""
+        "startup_id","ts","ts_end","dur","package","startup_type"
+        1,799979565075,800014194731,34629656,"com.google.android.googlequicksearchbox","[NULL]"
+        2,800868511677,800981929562,113417885,"androidx.benchmark.integration.macrobenchmark.target","[NULL]"
+        """))
+
+  def test_cold_startups_maxsdk28(self):
+    return DiffTestBlueprint(
+        trace=DataPath('api24_startup_cold.perfetto-trace'),
+        query="""
+        INCLUDE PERFETTO MODULE android.startup.startups;
+        SELECT * FROM android_startups;
+        """,
+        out=Csv("""
+        "startup_id","ts","ts_end","dur","package","startup_type"
+        1,791231114368,791501060868,269946500,"androidx.benchmark.integration.macrobenchmark.target","[NULL]"
+        """))