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

SELECT IMPORT('common.timestamps');

-- Converts a battery_stats counter value to human readable string.
--
-- @arg track STRING  The counter track name (e.g. 'battery_stats.audio').
-- @arg value LONG    The counter value.
-- @ret STRING        The human-readable name for the counter value.
SELECT CREATE_FUNCTION(
  'BATTERY_STATS_COUNTER_TO_STRING(track STRING, value FLOAT)',
  'STRING',
  '
  SELECT
    CASE
      WHEN ($track = "battery_stats.wifi_scan" OR
            $track = "battery_stats.wifi_radio" OR
            $track = "battery_stats.mobile_radio" OR
            $track = "battery_stats.audio" OR
            $track = "battery_stats.video" OR
            $track = "battery_stats.camera" OR
            $track = "battery_stats.power_save" OR
            $track = "battery_stats.phone_in_call")
        THEN
          CASE $value
            WHEN 0 THEN "inactive"
            WHEN 1 THEN "active"
            ELSE "unknown"
          END
      WHEN $track = "battery_stats.wifi"
        THEN
          CASE $value
            WHEN 0 THEN "off"
            WHEN 1 THEN "on"
            ELSE "unknown"
          END
      WHEN $track = "battery_stats.phone_state"
        THEN
          CASE $value
            WHEN 0 THEN "in"
            WHEN 1 THEN "out"
            WHEN 2 THEN "emergency"
            WHEN 3 THEN "off"
            ELSE "unknown"
          END
      WHEN ($track = "battery_stats.phone_signal_strength" OR
            $track = "battery_stats.wifi_signal_strength")
        THEN
          CASE $value
            WHEN 0 THEN "none"
            WHEN 1 THEN "poor"
            WHEN 2 THEN "moderate"
            WHEN 3 THEN "good"
            WHEN 4 THEN "great"
            ELSE "unknown"
          END
      WHEN $track = "battery_stats.wifi_suppl"
        THEN
          CASE $value
            WHEN 0 THEN "invalid"
            WHEN 1 THEN "disconn"
            WHEN 2 THEN "disabled"
            WHEN 3 THEN "inactive"
            WHEN 4 THEN "scanning"
            WHEN 5 THEN "authenticating"
            WHEN 6 THEN "associating"
            WHEN 7 THEN "associated"
            WHEN 8 THEN "4-way-handshake"
            WHEN 9 THEN "group-handshake"
            WHEN 10 THEN "completed"
            WHEN 11 THEN "dormant"
            WHEN 12 THEN "uninit"
            ELSE "unknown"
          END
      WHEN $track = "battery_stats.data_conn"
        THEN
          CASE $value
            WHEN 0 THEN "oos"
            WHEN 1 THEN "gprs"
            WHEN 2 THEN "edge"
            WHEN 3 THEN "umts"
            WHEN 4 THEN "cdma"
            WHEN 5 THEN "evdo_0"
            WHEN 6 THEN "evdo_A"
            WHEN 7 THEN "1xrtt"
            WHEN 8 THEN "hsdpa"
            WHEN 9 THEN "hsupa"
            WHEN 10 THEN "hspa"
            WHEN 11 THEN "iden"
            WHEN 12 THEN "evdo_b"
            WHEN 13 THEN "lte"
            WHEN 14 THEN "ehrpd"
            WHEN 15 THEN "hspap"
            WHEN 16 THEN "gsm"
            WHEN 17 THEN "td_scdma"
            WHEN 18 THEN "iwlan"
            WHEN 19 THEN "lte_ca"
            WHEN 20 THEN "nr"
            WHEN 21 THEN "emngcy"
            WHEN 22 THEN "other"
            ELSE "unknown"
          END
      ELSE CAST($value AS text)
    END
  '
);


-- View of human readable battery stats counter-based states. These are recorded
-- by BatteryStats as a bitmap where each 'category' has a unique value at any
-- given time.
--
-- @column ts                  Timestamp in nanoseconds.
-- @column dur                 The duration the state was active.
-- @column track_name          The name of the counter track.
-- @column value               The counter value as a number.
-- @column value_name          The counter value as a human-readable string.
CREATE VIEW android_battery_stats_state AS
SELECT
  ts,
  name AS track_name,
  CAST(value AS INT64) AS value,
  BATTERY_STATS_COUNTER_TO_STRING(name, value) AS value_name,
  IFNULL(LEAD(ts) OVER (PARTITION BY track_id ORDER BY ts) - ts, -1) AS dur
FROM counter
JOIN counter_track
  ON counter.track_id = counter_track.id
WHERE counter_track.name GLOB 'battery_stats.*';


-- View of slices derived from battery_stats events. Battery stats records all
-- events as instants, however some may indicate whether something started or
-- stopped with a '+' or '-' prefix. Events such as jobs, top apps, foreground
-- apps or long wakes include these details and allow drawing slices between
-- instant events found in a trace.
--
-- For example, we may see an event like the following on 'battery_stats.top':
--
--     -top=10215:"com.google.android.apps.nexuslauncher"
--
-- This view will find the associated start ('+top') with the matching suffix
-- (everything after the '=') to construct a slice. It computes the timestamp
-- and duration from the events and extract the details as follows:
--
--     track_name='battery_stats.top'
--     str_value='com.google.android.apps.nexuslauncher'
--     int_value=10215
--
-- @column track_name          The battery stats track name.
-- @column ts                  Timestamp in nanoseconds.
-- @column dur                 The duration of the event.
-- @column str_value           The string part of the event identifier.
-- @column int_value           The integer part of the event identifier.
CREATE VIEW android_battery_stats_event_slices AS
WITH
  event_markers AS (
    SELECT
      ts,
      track.name AS track_name,
      str_split(slice.name, '=', 1) AS key,
      substr(slice.name, 1, 1) = '+' AS start
    FROM slice
    JOIN track
      ON slice.track_id = track.id
    WHERE
      track_name GLOB 'battery_stats.*'
      AND substr(slice.name, 1, 1) IN ('+', '-')
  ),
  with_neighbors AS (
    SELECT
      *,
      LAG(ts) OVER (PARTITION BY track_name, key ORDER BY ts) AS last_ts,
      LEAD(ts) OVER (PARTITION BY track_name, key ORDER BY ts) AS next_ts
    FROM event_markers
  ),
  -- Note: query performance depends on the ability to push down filters on
  -- the track_name. It would be more clear below to have two queries and union
  -- them, but doing so prevents push down through the above window functions.
  event_spans AS (
    SELECT
      track_name, key,
      IIF(start, ts, TRACE_START()) AS ts,
      IIF(start, next_ts, ts) AS end_ts
    FROM with_neighbors
    -- For the majority of events, we take the `start` event and compute the dur
    -- based on next_ts. In the off chance we get an end event with no prior
    -- start (matched by the second half of this where), we can create an event
    -- starting from the beginning of the trace ending at the current event.
    WHERE (start OR last_ts IS NULL)
  )
SELECT
  ts,
  IFNULL(end_ts-ts, -1) AS dur,
  track_name,
  str_split(key, '"', 1) AS str_value,
  CAST(str_split(key, ':', 0) AS INT64) AS int_value
FROM event_spans;
