tp: Metric v2 implementation for Wattson

Implemented Wattson metric v2. Refactored and moved essential logics to
stdlib, exposing metric-specific logics in metric_specs.
Wattson metric v1 protos are heavily nested. v2 flattened the metric
proto structure and put most essential data into flat views for metricv2
consumption.

bug: 477599147
diff --git a/Android.bp b/Android.bp
index f3fa09e..7496e3d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15874,6 +15874,10 @@
         "src/trace_processor/perfetto_sql/stdlib/wattson/estimates.sql",
         "src/trace_processor/perfetto_sql/stdlib/wattson/gpu/estimates.sql",
         "src/trace_processor/perfetto_sql/stdlib/wattson/gpu/freq_idle.sql",
+        "src/trace_processor/perfetto_sql/stdlib/wattson/metrics/aggregation.sql",
+        "src/trace_processor/perfetto_sql/stdlib/wattson/metrics/rails_metrics.sql",
+        "src/trace_processor/perfetto_sql/stdlib/wattson/metrics/threads_metrics.sql",
+        "src/trace_processor/perfetto_sql/stdlib/wattson/metrics/windows.sql",
         "src/trace_processor/perfetto_sql/stdlib/wattson/tasks/attribution.sql",
         "src/trace_processor/perfetto_sql/stdlib/wattson/tasks/idle_transitions_attribution.sql",
         "src/trace_processor/perfetto_sql/stdlib/wattson/tasks/task_slices.sql",
diff --git a/BUILD b/BUILD
index efea722..caed96d 100644
--- a/BUILD
+++ b/BUILD
@@ -3871,6 +3871,10 @@
         "src/trace_processor/perfetto_sql/stdlib/wattson/estimates.sql",
         "src/trace_processor/perfetto_sql/stdlib/wattson/gpu/estimates.sql",
         "src/trace_processor/perfetto_sql/stdlib/wattson/gpu/freq_idle.sql",
+        "src/trace_processor/perfetto_sql/stdlib/wattson/metrics/aggregation.sql",
+        "src/trace_processor/perfetto_sql/stdlib/wattson/metrics/rails_metrics.sql",
+        "src/trace_processor/perfetto_sql/stdlib/wattson/metrics/threads_metrics.sql",
+        "src/trace_processor/perfetto_sql/stdlib/wattson/metrics/windows.sql",
         "src/trace_processor/perfetto_sql/stdlib/wattson/tasks/attribution.sql",
         "src/trace_processor/perfetto_sql/stdlib/wattson/tasks/idle_transitions_attribution.sql",
         "src/trace_processor/perfetto_sql/stdlib/wattson/tasks/task_slices.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/wattson/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/wattson/BUILD.gn
index 264ac9c..9f4f10e 100644
--- a/src/trace_processor/perfetto_sql/stdlib/wattson/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/wattson/BUILD.gn
@@ -37,6 +37,10 @@
     "estimates.sql",
     "gpu/estimates.sql",
     "gpu/freq_idle.sql",
+    "metrics/aggregation.sql",
+    "metrics/rails_metrics.sql",
+    "metrics/threads_metrics.sql",
+    "metrics/windows.sql",
     "tasks/attribution.sql",
     "tasks/idle_transitions_attribution.sql",
     "tasks/task_slices.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/wattson/metrics/aggregation.sql b/src/trace_processor/perfetto_sql/stdlib/wattson/metrics/aggregation.sql
new file mode 100644
index 0000000..9bc30ab
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/wattson/metrics/aggregation.sql
@@ -0,0 +1,427 @@
+--
+-- Copyright 2026 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.
+
+INCLUDE PERFETTO MODULE intervals.intersect;
+
+INCLUDE PERFETTO MODULE wattson.cpu.idle;
+
+INCLUDE PERFETTO MODULE wattson.device_infos;
+
+INCLUDE PERFETTO MODULE wattson.estimates;
+
+INCLUDE PERFETTO MODULE wattson.tasks.attribution;
+
+INCLUDE PERFETTO MODULE wattson.tasks.idle_transitions_attribution;
+
+INCLUDE PERFETTO MODULE wattson.utils;
+
+-- ========================================================
+-- MACRO: _wattson_threads_build_flat_view
+--
+-- Calculates energy and power attribution per thread/process for the
+-- given time windows.
+--
+-- Input:
+--   window_table: A table with columns (ts, dur, period_id).
+--
+-- Output:
+--   Flat table with columns:
+--     period_id, period_dur, utid, tid, pid,
+--     thread_name, process_name,
+--     estimated_mws, estimated_mw, idle_transitions_mws, total_mws
+-- ========================================================
+CREATE PERFETTO MACRO _wattson_threads_build_flat_view(
+    window_table TableOrSubquery
+)
+RETURNS TableOrSubquery AS
+(
+  -- Active Power Intersection (Intersection of Task Power & Window)
+  WITH
+    windowed_active_state AS (
+      SELECT
+        ii.dur,
+        ii.id_1 AS period_id,
+        tasks.estimated_mw,
+        tasks.thread_name,
+        tasks.process_name,
+        tasks.tid,
+        tasks.pid,
+        tasks.utid
+      FROM _interval_intersect!(
+      (
+        _ii_subquery!(_estimates_w_tasks_attribution),
+        (SELECT period_id AS id, * FROM $window_table)
+      ),
+      ()
+    ) AS ii
+      JOIN _estimates_w_tasks_attribution AS tasks
+        ON tasks._auto_id = id_0
+    ),
+    -- Aggregate Active Power per Thread per Period
+    active_summary AS (
+      SELECT
+        period_id,
+        utid,
+        -- Metadata (take min/max as they are constant per utid)
+        min(thread_name) AS thread_name,
+        min(process_name) AS process_name,
+        min(tid) AS tid,
+        min(pid) AS pid,
+        -- Calculations
+        sum(estimated_mw * dur) / 1e9 AS active_mws,
+        -- Keep for power calc
+        sum(estimated_mw * dur) AS total_mw_ns
+      FROM windowed_active_state
+      GROUP BY
+        period_id,
+        utid
+    ),
+    -- Calculate Idle Cost (Join against the specific window constraints)
+    idle_summary AS (
+      SELECT
+        w.period_id,
+        cost.utid,
+        sum(cost.idle_cost_mws) AS idle_mws
+      FROM $window_table AS w, _filter_idle_attribution(w.ts, w.dur) AS cost
+      GROUP BY
+        w.period_id,
+        cost.utid
+    )
+  -- Final Join & Calculation
+  SELECT
+    a.period_id,
+    w.dur AS period_dur,
+    a.utid,
+    a.tid,
+    a.pid,
+    coalesce(a.thread_name, 'Thread ' || a.tid) AS thread_name,
+    coalesce(a.process_name, '') AS process_name,
+    -- Metrics
+    a.active_mws AS estimated_mws,
+    -- Power = Energy / Window Duration
+    (
+      a.total_mw_ns / w.dur
+    ) AS estimated_mw,
+    coalesce(i.idle_mws, 0) AS idle_transitions_mws,
+    (
+      a.active_mws + coalesce(i.idle_mws, 0)
+    ) AS total_mws
+  FROM active_summary AS a
+  JOIN $window_table AS w
+    ON a.period_id = w.period_id
+  LEFT JOIN idle_summary AS i
+    ON a.period_id = i.period_id AND a.utid = i.utid
+);
+
+-- ========================================================
+-- MACRO: _wattson_base_components_avg_mw
+--
+-- Low-level macro to calculate base power components average mW.
+--
+-- Input:
+--   window_table: A table with columns (ts, dur, period_id).
+--
+-- Output:
+--   Wide table with CPU policy, average power per core, DSU, and GPU.
+-- ========================================================
+CREATE PERFETTO MACRO _wattson_base_components_avg_mw(
+    window_table TableOrSubquery
+)
+RETURNS TableOrSubquery AS
+(
+  SELECT
+    (
+      SELECT
+        m.policy
+      FROM _dev_cpu_policy_map AS m
+      WHERE
+        m.cpu = 0
+    ) AS cpu0_poli,
+    (
+      SELECT
+        m.policy
+      FROM _dev_cpu_policy_map AS m
+      WHERE
+        m.cpu = 1
+    ) AS cpu1_poli,
+    (
+      SELECT
+        m.policy
+      FROM _dev_cpu_policy_map AS m
+      WHERE
+        m.cpu = 2
+    ) AS cpu2_poli,
+    (
+      SELECT
+        m.policy
+      FROM _dev_cpu_policy_map AS m
+      WHERE
+        m.cpu = 3
+    ) AS cpu3_poli,
+    (
+      SELECT
+        m.policy
+      FROM _dev_cpu_policy_map AS m
+      WHERE
+        m.cpu = 4
+    ) AS cpu4_poli,
+    (
+      SELECT
+        m.policy
+      FROM _dev_cpu_policy_map AS m
+      WHERE
+        m.cpu = 5
+    ) AS cpu5_poli,
+    (
+      SELECT
+        m.policy
+      FROM _dev_cpu_policy_map AS m
+      WHERE
+        m.cpu = 6
+    ) AS cpu6_poli,
+    (
+      SELECT
+        m.policy
+      FROM _dev_cpu_policy_map AS m
+      WHERE
+        m.cpu = 7
+    ) AS cpu7_poli,
+    sum(ii.dur * ss.cpu0_mw) / nullif(sum(ii.dur), 0) AS cpu0_mw,
+    sum(ii.dur * ss.cpu1_mw) / nullif(sum(ii.dur), 0) AS cpu1_mw,
+    sum(ii.dur * ss.cpu2_mw) / nullif(sum(ii.dur), 0) AS cpu2_mw,
+    sum(ii.dur * ss.cpu3_mw) / nullif(sum(ii.dur), 0) AS cpu3_mw,
+    sum(ii.dur * ss.cpu4_mw) / nullif(sum(ii.dur), 0) AS cpu4_mw,
+    sum(ii.dur * ss.cpu5_mw) / nullif(sum(ii.dur), 0) AS cpu5_mw,
+    sum(ii.dur * ss.cpu6_mw) / nullif(sum(ii.dur), 0) AS cpu6_mw,
+    sum(ii.dur * ss.cpu7_mw) / nullif(sum(ii.dur), 0) AS cpu7_mw,
+    sum(ii.dur * ss.dsu_scu_mw) / nullif(sum(ii.dur), 0) AS dsu_scu_mw,
+    sum(ii.dur * ss.gpu_mw) / nullif(sum(ii.dur), 0) AS gpu_mw,
+    sum(ii.dur) AS period_dur,
+    ii.id_0 AS period_id
+  FROM _interval_intersect!(
+    (
+      (SELECT period_id AS id, * FROM $window_table),
+      _ii_subquery!(_system_state_mw)
+    ),
+    ()
+  ) AS ii
+  JOIN _system_state_mw AS ss
+    ON ss._auto_id = id_1
+  GROUP BY
+    period_id
+);
+
+-- ========================================================
+-- MACRO: _wattson_rail_build_flat_view
+--
+-- Flattening and unpivoting of rail data into a standard breakdown.
+--
+-- Input:
+--   window_table: A table with columns (ts, dur, period_id).
+--
+-- Output:
+--   Flat breakdown including CORE, POLICY, DSU and SUBSYSTEM TOTAL.
+-- ========================================================
+CREATE PERFETTO MACRO _wattson_rail_build_flat_view(
+    window_table TableOrSubquery
+)
+RETURNS TableOrSubquery AS
+(
+  -- 1. Cache base components
+  WITH
+    base_components AS (
+      SELECT
+        *
+      FROM _wattson_base_components_avg_mw!($window_table)
+    ),
+    -- 2. Unpivot CPU columns
+    cpu_unpivoted AS (
+      SELECT
+        period_id,
+        period_dur,
+        0 AS cpu_id,
+        cpu0_poli AS policy_id,
+        cpu0_mw AS mw
+      FROM base_components
+      WHERE
+        cpu0_mw IS NOT NULL
+      UNION ALL
+      SELECT
+        period_id,
+        period_dur,
+        1,
+        cpu1_poli,
+        cpu1_mw
+      FROM base_components
+      WHERE
+        cpu1_mw IS NOT NULL
+      UNION ALL
+      SELECT
+        period_id,
+        period_dur,
+        2,
+        cpu2_poli,
+        cpu2_mw
+      FROM base_components
+      WHERE
+        cpu2_mw IS NOT NULL
+      UNION ALL
+      SELECT
+        period_id,
+        period_dur,
+        3,
+        cpu3_poli,
+        cpu3_mw
+      FROM base_components
+      WHERE
+        cpu3_mw IS NOT NULL
+      UNION ALL
+      SELECT
+        period_id,
+        period_dur,
+        4,
+        cpu4_poli,
+        cpu4_mw
+      FROM base_components
+      WHERE
+        cpu4_mw IS NOT NULL
+      UNION ALL
+      SELECT
+        period_id,
+        period_dur,
+        5,
+        cpu5_poli,
+        cpu5_mw
+      FROM base_components
+      WHERE
+        cpu5_mw IS NOT NULL
+      UNION ALL
+      SELECT
+        period_id,
+        period_dur,
+        6,
+        cpu6_poli,
+        cpu6_mw
+      FROM base_components
+      WHERE
+        cpu6_mw IS NOT NULL
+      UNION ALL
+      SELECT
+        period_id,
+        period_dur,
+        7,
+        cpu7_poli,
+        cpu7_mw
+      FROM base_components
+      WHERE
+        cpu7_mw IS NOT NULL
+    ),
+    -- 3. Build basic Flat View (encapsulate in CTE to reuse logic for Total calculation)
+    flat_view_raw AS (
+      -- A. CPU Cores
+      SELECT
+        CAST(c.period_id AS STRING) AS period_name,
+        'CPU' AS subsystem,
+        'CORE' AS breakdown_type,
+        c.cpu_id AS component_id,
+        c.policy_id AS parent_id,
+        c.mw AS estimated_mw,
+        (
+          c.mw * c.period_dur / 1e9
+        ) AS estimated_mws
+      FROM cpu_unpivoted AS c
+      UNION ALL
+      -- B. CPU Policies
+      SELECT
+        CAST(c.period_id AS STRING) AS period_name,
+        'CPU' AS subsystem,
+        'POLICY' AS breakdown_type,
+        c.policy_id AS component_id,
+        NULL AS parent_id,
+        sum(c.mw) AS estimated_mw,
+        sum(c.mw * c.period_dur / 1e9) AS estimated_mws
+      FROM cpu_unpivoted AS c
+      GROUP BY
+        c.period_id,
+        c.period_dur,
+        c.policy_id
+      UNION ALL
+      -- C. DSU/SCU
+      SELECT
+        CAST(base.period_id AS STRING) AS period_name,
+        'CPU' AS subsystem,
+        'DSU' AS breakdown_type,
+        NULL AS component_id,
+        NULL AS parent_id,
+        base.dsu_scu_mw AS estimated_mw,
+        (
+          base.dsu_scu_mw * base.period_dur / 1e9
+        ) AS estimated_mws
+      FROM base_components AS base
+      WHERE
+        base.dsu_scu_mw IS NOT NULL
+      UNION ALL
+      -- D. GPU Subsystem
+      SELECT
+        CAST(base.period_id AS STRING) AS period_name,
+        'GPU' AS subsystem,
+        'TOTAL' AS breakdown_type,
+        NULL AS component_id,
+        NULL AS parent_id,
+        base.gpu_mw AS estimated_mw,
+        (
+          base.gpu_mw * base.period_dur / 1e9
+        ) AS estimated_mws
+      FROM base_components AS base
+      WHERE
+        base.gpu_mw IS NOT NULL
+    )
+  -- 4. Final output: Raw Data + Computed CPU Total
+  SELECT
+    *
+  FROM flat_view_raw
+  UNION ALL
+  -- E. CPU TOTAL (Auto-calculated)
+  -- Sum only Policy and DSU (exclude Cores to avoid double counting)
+  SELECT
+    period_name,
+    subsystem,
+    'TOTAL' AS breakdown_type,
+    NULL AS component_id,
+    NULL AS parent_id,
+    sum(estimated_mw) AS estimated_mw,
+    sum(estimated_mws) AS estimated_mws
+  FROM flat_view_raw
+  WHERE
+    subsystem = 'CPU' AND breakdown_type IN ('POLICY', 'DSU')
+  GROUP BY
+    period_name,
+    subsystem
+);
+
+-- ========================================================
+-- VIEW: _wattson_metric_metadata
+--
+-- Shared metadata for all Wattson metrics.
+-- ========================================================
+CREATE PERFETTO VIEW _wattson_metric_metadata AS
+SELECT
+  4 AS metric_version,
+  1 AS power_model_version,
+  CAST(NOT EXISTS(
+    SELECT
+      1
+    FROM _wattson_cpuidle_counters_exist
+  ) AS INTEGER) AS is_crude_estimate;
diff --git a/src/trace_processor/perfetto_sql/stdlib/wattson/metrics/rails_metrics.sql b/src/trace_processor/perfetto_sql/stdlib/wattson/metrics/rails_metrics.sql
new file mode 100644
index 0000000..3d29d1f
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/wattson/metrics/rails_metrics.sql
@@ -0,0 +1,46 @@
+--
+-- Copyright 2026 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.
+
+INCLUDE PERFETTO MODULE wattson.metrics.aggregation;
+
+INCLUDE PERFETTO MODULE wattson.metrics.windows;
+
+-- ========================================================
+-- Wattson rail aggregation views for various time windows.
+-- ========================================================
+
+-- Wattson rail aggregation for Marker window
+CREATE PERFETTO VIEW _wattson_rails_marker AS
+SELECT
+  *
+FROM _wattson_rail_build_flat_view!(_wattson_window_marker);
+
+-- Wattson rail aggregation for Full Trace
+CREATE PERFETTO VIEW _wattson_rails_trace AS
+SELECT
+  *
+FROM _wattson_rail_build_flat_view!(_wattson_window_trace);
+
+-- Wattson rail aggregation for Startup
+CREATE PERFETTO VIEW _wattson_rails_startup AS
+SELECT
+  *
+FROM _wattson_rail_build_flat_view!(_wattson_window_startup);
+
+-- Wattson rail aggregation for CUJ
+CREATE PERFETTO VIEW _wattson_rails_cuj AS
+SELECT
+  *
+FROM _wattson_rail_build_flat_view!(_wattson_window_cuj);
diff --git a/src/trace_processor/perfetto_sql/stdlib/wattson/metrics/threads_metrics.sql b/src/trace_processor/perfetto_sql/stdlib/wattson/metrics/threads_metrics.sql
new file mode 100644
index 0000000..3caaa54
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/wattson/metrics/threads_metrics.sql
@@ -0,0 +1,46 @@
+--
+-- Copyright 2026 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.
+
+INCLUDE PERFETTO MODULE wattson.metrics.aggregation;
+
+INCLUDE PERFETTO MODULE wattson.metrics.windows;
+
+-- ========================================================
+-- Wattson thread aggregation views for various time windows.
+-- ========================================================
+
+-- Wattson thread aggregation for Marker window
+CREATE PERFETTO VIEW _wattson_threads_marker AS
+SELECT
+  *
+FROM _wattson_threads_build_flat_view!(_wattson_window_marker);
+
+-- Wattson thread aggregation for Full Trace
+CREATE PERFETTO VIEW _wattson_threads_trace AS
+SELECT
+  *
+FROM _wattson_threads_build_flat_view!(_wattson_window_trace);
+
+-- Wattson thread aggregation for Startup
+CREATE PERFETTO VIEW _wattson_threads_startup AS
+SELECT
+  *
+FROM _wattson_threads_build_flat_view!(_wattson_window_startup);
+
+-- Wattson thread aggregation for CUJ
+CREATE PERFETTO VIEW _wattson_threads_cuj AS
+SELECT
+  *
+FROM _wattson_threads_build_flat_view!(_wattson_window_cuj);
diff --git a/src/trace_processor/perfetto_sql/stdlib/wattson/metrics/windows.sql b/src/trace_processor/perfetto_sql/stdlib/wattson/metrics/windows.sql
new file mode 100644
index 0000000..905a2ee
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/wattson/metrics/windows.sql
@@ -0,0 +1,55 @@
+--
+-- Copyright 2026 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.
+
+INCLUDE PERFETTO MODULE android.cujs.base;
+
+INCLUDE PERFETTO MODULE android.startup.startups;
+
+INCLUDE PERFETTO MODULE wattson.utils;
+
+-- ========================================================
+-- Window definitions for Wattson metric analysis.
+-- ========================================================
+
+-- Standardized window for Wattson markers
+CREATE PERFETTO VIEW _wattson_window_marker AS
+SELECT
+  ts,
+  dur,
+  1 AS period_id
+FROM _wattson_markers_window;
+
+-- Standardized window for the full trace duration
+CREATE PERFETTO VIEW _wattson_window_trace AS
+SELECT
+  trace_start() AS ts,
+  trace_dur() AS dur,
+  1 AS period_id;
+
+-- Standardized window for Android app startups
+CREATE PERFETTO VIEW _wattson_window_startup AS
+SELECT
+  ts,
+  dur,
+  startup_id AS period_id
+FROM android_startups;
+
+-- Standardized window for Android CUJs
+CREATE PERFETTO VIEW _wattson_window_cuj AS
+SELECT
+  ts,
+  dur,
+  cuj_id AS period_id
+FROM android_jank_cuj;
diff --git a/src/trace_processor/perfetto_sql/stdlib/wattson/textprotos/rails.textproto b/src/trace_processor/perfetto_sql/stdlib/wattson/textprotos/rails.textproto
new file mode 100644
index 0000000..b2d03ae
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/wattson/textprotos/rails.textproto
@@ -0,0 +1,319 @@
+#
+# Copyright 2026 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.
+
+metric_spec {
+  id: "wattson_metadata"
+  dimensions: "metric_version"
+  dimensions: "power_model_version"
+  value: "is_crude_estimate"
+  query: {
+    table: {
+      table_name: "_wattson_metric_metadata"
+    }
+    referenced_modules: "wattson.metrics.aggregation"
+  }
+}
+
+metric_template_spec {
+  id_prefix: "wattson_rails_marker"
+
+  # ==========================================================
+  # Dimensions
+  # ==========================================================
+
+  # 1. Period Identifier
+  dimensions_specs {
+    name: "period_name"
+    type: STRING
+  }
+
+  # 2. Hardware Subsystem
+  dimensions_specs {
+    name: "subsystem"
+    type: STRING
+  }
+
+  # 3. Hierarchy Type
+  dimensions_specs {
+    name: "breakdown_type"
+    type: STRING
+  }
+
+  # 4. Component Identifier
+  dimensions_specs {
+    name: "component_id"
+    type: INT64
+    display_name: "ID"
+  }
+
+  # 5. Parent Relation
+  dimensions_specs {
+    name: "parent_id"
+    type: INT64
+    display_name: "Cluster ID"
+  }
+
+  # ==========================================================
+  # Values
+  # ==========================================================
+
+  # Metric 1: Energy
+  value_column_specs {
+    name: "estimated_mws"
+    display_name: "Energy (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+
+  # Metric 2: Power
+  value_column_specs {
+    name: "estimated_mw"
+    display_name: "Average Power"
+    unit: MILLI_WATTS
+    polarity: LOWER_IS_BETTER
+  }
+
+  # ==========================================================
+  # Query (Data Source)
+  # ==========================================================
+
+  query {
+    table {
+      table_name: "_wattson_rails_marker"
+    }
+    referenced_modules: "wattson.metrics.rails_metrics"
+  }
+
+  dimension_uniqueness: UNIQUE
+}
+
+metric_template_spec {
+  id_prefix: "wattson_rails_trace"
+
+  # ==========================================================
+  # Dimensions
+  # ==========================================================
+
+  # 1. Period Identifier
+  dimensions_specs {
+    name: "period_name"
+    type: STRING
+  }
+
+  # 2. Hardware Subsystem
+  dimensions_specs {
+    name: "subsystem"
+    type: STRING
+  }
+
+  # 3. Hierarchy Type
+  dimensions_specs {
+    name: "breakdown_type"
+    type: STRING
+  }
+
+  # 4. Component Identifier
+  dimensions_specs {
+    name: "component_id"
+    type: INT64
+    display_name: "ID"
+  }
+
+  # 5. Parent Relation
+  dimensions_specs {
+    name: "parent_id"
+    type: INT64
+    display_name: "Cluster ID"
+  }
+
+  # ==========================================================
+  # Values
+  # ==========================================================
+
+  # Metric 1: Energy
+  value_column_specs {
+    name: "estimated_mws"
+    display_name: "Energy (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+
+  # Metric 2: Power
+  value_column_specs {
+    name: "estimated_mw"
+    display_name: "Average Power"
+    unit: MILLI_WATTS
+    polarity: LOWER_IS_BETTER
+  }
+
+  # ==========================================================
+  # Query (Data Source)
+  # ==========================================================
+
+  query {
+    table {
+      table_name: "_wattson_rails_trace"
+    }
+    referenced_modules: "wattson.metrics.rails_metrics"
+  }
+
+  dimension_uniqueness: UNIQUE
+}
+
+metric_template_spec {
+  id_prefix: "wattson_rails_startup"
+
+  # ==========================================================
+  # Dimensions
+  # ==========================================================
+
+  # 1. Period Identifier
+  dimensions_specs {
+    name: "period_name"
+    type: STRING
+  }
+
+  # 2. Hardware Subsystem
+  dimensions_specs {
+    name: "subsystem"
+    type: STRING
+  }
+
+  # 3. Hierarchy Type
+  dimensions_specs {
+    name: "breakdown_type"
+    type: STRING
+  }
+
+  # 4. Component Identifier
+  dimensions_specs {
+    name: "component_id"
+    type: INT64
+    display_name: "ID"
+  }
+
+  # 5. Parent Relation
+  dimensions_specs {
+    name: "parent_id"
+    type: INT64
+    display_name: "Cluster ID"
+  }
+
+  # ==========================================================
+  # Values
+  # ==========================================================
+
+  # Metric 1: Energy
+  value_column_specs {
+    name: "estimated_mws"
+    display_name: "Energy (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+
+  # Metric 2: Power
+  value_column_specs {
+    name: "estimated_mw"
+    display_name: "Average Power"
+    unit: MILLI_WATTS
+    polarity: LOWER_IS_BETTER
+  }
+
+  # ==========================================================
+  # Query (Data Source)
+  # ==========================================================
+
+  query {
+    table {
+      table_name: "_wattson_rails_startup"
+    }
+    referenced_modules: "wattson.metrics.rails_metrics"
+  }
+
+  dimension_uniqueness: UNIQUE
+}
+
+metric_template_spec {
+  id_prefix: "wattson_rails_cuj"
+
+  # ==========================================================
+  # Dimensions
+  # ==========================================================
+
+  # 1. Period Identifier
+  dimensions_specs {
+    name: "period_name"
+    type: STRING
+  }
+
+  # 2. Hardware Subsystem
+  dimensions_specs {
+    name: "subsystem"
+    type: STRING
+  }
+
+  # 3. Hierarchy Type
+  dimensions_specs {
+    name: "breakdown_type"
+    type: STRING
+  }
+
+  # 4. Component Identifier
+  dimensions_specs {
+    name: "component_id"
+    type: INT64
+    display_name: "ID"
+  }
+
+  # 5. Parent Relation
+  dimensions_specs {
+    name: "parent_id"
+    type: INT64
+    display_name: "Cluster ID"
+  }
+
+  # ==========================================================
+  # Values
+  # ==========================================================
+
+  # Metric 1: Energy
+  value_column_specs {
+    name: "estimated_mws"
+    display_name: "Energy (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+
+  # Metric 2: Power
+  value_column_specs {
+    name: "estimated_mw"
+    display_name: "Average Power"
+    unit: MILLI_WATTS
+    polarity: LOWER_IS_BETTER
+  }
+
+  # ==========================================================
+  # Query (Data Source)
+  # ==========================================================
+
+  query {
+    table {
+      table_name: "_wattson_rails_cuj"
+    }
+    referenced_modules: "wattson.metrics.rails_metrics"
+  }
+
+  dimension_uniqueness: UNIQUE
+}
diff --git a/src/trace_processor/perfetto_sql/stdlib/wattson/textprotos/threads.textproto b/src/trace_processor/perfetto_sql/stdlib/wattson/textprotos/threads.textproto
new file mode 100644
index 0000000..79386dc
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/wattson/textprotos/threads.textproto
@@ -0,0 +1,227 @@
+#
+# Copyright 2026 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.
+
+metric_spec {
+  id: "wattson_metadata"
+  dimensions: "metric_version"
+  dimensions: "power_model_version"
+  value: "is_crude_estimate"
+  query: {
+    table: {
+      table_name: "_wattson_metric_metadata"
+    }
+    referenced_modules: "wattson.metrics.aggregation"
+  }
+}
+
+# Marker
+metric_template_spec {
+  id_prefix: "wattson_threads_marker"
+
+  # --- Dimensions ---
+  # 1. Period
+  dimensions_specs { name: "period_name" type: STRING }
+  dimensions_specs { name: "period_id" type: INT64 }
+
+  # 2. Process Info
+  dimensions_specs { name: "process_name" type: STRING }
+  dimensions_specs { name: "pid" type: INT64 }
+
+  # 3. Thread Info
+  dimensions_specs { name: "thread_name" type: STRING }
+  dimensions_specs { name: "tid" type: INT64 }
+
+  # --- Values ---
+  value_column_specs {
+    name: "estimated_mws"
+    display_name: "Active Energy (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+  value_column_specs {
+    name: "estimated_mw"
+    display_name: "Active Power"
+    unit: MILLI_WATTS
+    polarity: LOWER_IS_BETTER
+  }
+  value_column_specs {
+    name: "idle_transitions_mws"
+    display_name: "Idle Overhead (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+  value_column_specs {
+    name: "total_mws"
+    display_name: "Total Energy (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+
+  # --- Source ---
+  query {
+    table { table_name: "_wattson_threads_marker" }
+    referenced_modules: "wattson.metrics.threads_metrics"
+  }
+}
+
+# Full Trace
+metric_template_spec {
+  id_prefix: "wattson_threads_trace"
+
+  # --- Dimensions ---
+  # 1. Period
+  dimensions_specs { name: "period_name" type: STRING }
+  dimensions_specs { name: "period_id" type: INT64 }
+
+  # 2. Process Info
+  dimensions_specs { name: "process_name" type: STRING }
+  dimensions_specs { name: "pid" type: INT64 }
+
+  # 3. Thread Info
+  dimensions_specs { name: "thread_name" type: STRING }
+  dimensions_specs { name: "tid" type: INT64 }
+
+  # --- Values ---
+  value_column_specs {
+    name: "estimated_mws"
+    display_name: "Active Energy (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+  value_column_specs {
+    name: "estimated_mw"
+    display_name: "Active Power"
+    unit: MILLI_WATTS
+    polarity: LOWER_IS_BETTER
+  }
+  value_column_specs {
+    name: "idle_transitions_mws"
+    display_name: "Idle Overhead (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+  value_column_specs {
+    name: "total_mws"
+    display_name: "Total Energy (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+
+  # --- Source ---
+  query {
+    table { table_name: "_wattson_threads_trace" }
+    referenced_modules: "wattson.metrics.threads_metrics"
+  }
+}
+
+# App Startup
+metric_template_spec {
+  id_prefix: "wattson_threads_startup"
+
+  # --- Dimensions ---
+  # 1. Period
+  dimensions_specs { name: "period_name" type: STRING }
+  dimensions_specs { name: "period_id" type: INT64 }
+
+  # 2. Process Info
+  dimensions_specs { name: "process_name" type: STRING }
+  dimensions_specs { name: "pid" type: INT64 }
+
+  # 3. Thread Info
+  dimensions_specs { name: "thread_name" type: STRING }
+  dimensions_specs { name: "tid" type: INT64 }
+
+  # --- Values ---
+  value_column_specs {
+    name: "estimated_mws"
+    display_name: "Active Energy (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+  value_column_specs {
+    name: "estimated_mw"
+    display_name: "Active Power"
+    unit: MILLI_WATTS
+    polarity: LOWER_IS_BETTER
+  }
+  value_column_specs {
+    name: "idle_transitions_mws"
+    display_name: "Idle Overhead (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+  value_column_specs {
+    name: "total_mws"
+    display_name: "Total Energy (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+
+  # --- Source ---
+  query {
+    table { table_name: "_wattson_threads_startup" }
+    referenced_modules: "wattson.metrics.threads_metrics"
+  }
+}
+
+# CUJ
+metric_template_spec {
+  id_prefix: "wattson_threads_cuj"
+
+  # --- Dimensions ---
+  # 1. Period
+  dimensions_specs { name: "period_name" type: STRING }
+  dimensions_specs { name: "period_id" type: INT64 }
+
+  # 2. Process Info
+  dimensions_specs { name: "process_name" type: STRING }
+  dimensions_specs { name: "pid" type: INT64 }
+
+  # 3. Thread Info
+  dimensions_specs { name: "thread_name" type: STRING }
+  dimensions_specs { name: "tid" type: INT64 }
+
+  # --- Values ---
+  value_column_specs {
+    name: "estimated_mws"
+    display_name: "Active Energy (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+  value_column_specs {
+    name: "estimated_mw"
+    display_name: "Active Power"
+    unit: MILLI_WATTS
+    polarity: LOWER_IS_BETTER
+  }
+  value_column_specs {
+    name: "idle_transitions_mws"
+    display_name: "Idle Overhead (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+  value_column_specs {
+    name: "total_mws"
+    display_name: "Total Energy (mWs)"
+    custom_unit: "mWs"
+    polarity: LOWER_IS_BETTER
+  }
+
+  # --- Source ---
+  query {
+    table { table_name: "_wattson_threads_cuj" }
+    referenced_modules: "wattson.metrics.threads_metrics"
+  }
+}