--
-- Copyright 2025 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.

-- This module primarily performs composition between the blocking calls, frames and CUJs.
-- This is used for capturing blocking call per frame metrics, and the related plugins.
INCLUDE PERFETTO MODULE android.critical_blocking_calls;

INCLUDE PERFETTO MODULE android.cujs.base;

-- For cases when a blocking call starts within a frame, but does not end before the actual frame
-- ends, a part of the blocking call can be missed while calculating the metric. To avoid this
-- issue, the frame boundary is extended, spanning from the start of the current actual frame, till
-- the start of either next actual frame or the next doFrame slice - whichever is earliest;
-- we are relying on both types of slices to get more stable metrics for cases where, for instance,
-- doFrame blocks are running without any visible changes on the screen - leading to
-- no actual frames being drawn. When this happens there might be gaps in actual frame timeline,
-- so then the next doFrame would be resolved as an earlier boundary for an extended frame.
CREATE PERFETTO VIEW _extended_frame_boundary AS
-- Get the start time of the NEXT frame in the sequence
WITH
  _frames_with_next AS (
    SELECT
      *,
      lead(frame_ts) OVER (PARTITION BY cuj_id ORDER BY frame_id) AS next_frame_start
    FROM _android_distinct_frames_in_cuj
  ),
  _frames_with_extension_options AS (
    SELECT
      fr.frame_id,
      fr.frame_ts AS ts,
      fr.ui_thread_utid,
      fr.render_thread_utid,
      fr.cuj_id,
      fr.cuj_name,
      fr.layer_id,
      -- Calculate the first doFrame start that occurs AFTER the current frame ends
      (
        SELECT min(ts)
        FROM _android_jank_cuj_do_frames
        WHERE
          utid = fr.ui_thread_utid
          AND ts >= fr.ts_end
          AND ts <= fr.next_frame_start
      ) AS next_do_slice_after_frame,
      fr.next_frame_start,
      fr.ts_end AS original_ts_end
    FROM _frames_with_next AS fr
  )
SELECT
  ts,
  ui_thread_utid,
  render_thread_utid,
  frame_id,
  layer_id,
  cuj_id,
  cuj_name,
  -- Earliest of (Next Frame Start, Next doFrame Start).
  -- Fallback to original ts_end only if both are NULL.
  coalesce(
    min(next_do_slice_after_frame, next_frame_start),
    next_do_slice_after_frame,
    next_frame_start,
    original_ts_end
  ) AS ts_end
FROM _frames_with_extension_options
ORDER BY
  frame_id;

-- Capture blocking call duration within frames within a CUJ.
CREATE PERFETTO VIEW _blocking_calls_frame_cuj AS
SELECT
  min(bc.dur, frame.ts_end - bc.ts, bc.ts_end - frame.ts) AS dur,
  max(frame.ts, bc.ts) AS ts,
  bc.upid,
  bc.name,
  bc.process_name,
  bc.utid,
  frame.frame_id,
  frame.layer_id,
  cuj_id,
  cuj_name
FROM _android_critical_blocking_calls AS bc
JOIN _extended_frame_boundary AS frame
  ON (bc.utid = frame.ui_thread_utid OR bc.utid = frame.render_thread_utid)
-- The following condition to accommodate blocking call crossing frame boundary. The blocking
-- call starts in a frame or ends in a frame. It can either be the same frame or a different
-- frame.
WHERE
  (
  -- Blocking call starts within the frame.
  (bc.ts >= frame.ts AND bc.ts <= frame.ts_end)
  OR (bc.ts_end >= frame.ts AND bc.ts_end <= frame.ts_end));
