-- -- Copyright 2019 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. -- DROP VIEW IF EXISTS battery_view; CREATE VIEW battery_view AS SELECT all_ts.ts as ts, current_avg_ua, capacity_percent, charge_uah, current_ua FROM ( SELECT distinct(ts) AS ts FROM counter c JOIN counter_track t on c.track_id = t.id WHERE name LIKE 'batt.%' ) AS all_ts LEFT JOIN ( SELECT ts, value AS current_avg_ua FROM counter c JOIN counter_track t on c.track_id = t.id WHERE name='batt.current.avg_ua' ) USING(ts) LEFT JOIN ( SELECT ts, value AS capacity_percent FROM counter c JOIN counter_track t on c.track_id = t.id WHERE name='batt.capacity_pct' ) USING(ts) LEFT JOIN ( SELECT ts, value AS charge_uah FROM counter c JOIN counter_track t on c.track_id = t.id WHERE name='batt.charge_uah' ) USING(ts) LEFT JOIN ( SELECT ts, value AS current_ua FROM counter c JOIN counter_track t on c.track_id = t.id WHERE name='batt.current_ua' ) USING(ts) ORDER BY ts; DROP TABLE IF EXISTS android_batt_wakelocks_merged; CREATE TABLE android_batt_wakelocks_merged AS SELECT MIN(ts) AS ts, MAX(ts_end) AS ts_end FROM ( SELECT *, SUM(new_group) OVER (ORDER BY ts) AS group_id FROM ( SELECT ts, ts + dur AS ts_end, -- There is a new group if there was a gap before this wakelock. -- i.e. the max end timestamp of all preceding wakelocks is before -- the start timestamp of this one. -- The null check is for the first row which is always a new group. IFNULL( MAX(ts + dur) OVER ( ORDER BY ts ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) < ts, true ) AS new_group FROM slice WHERE slice.name LIKE 'WakeLock %' AND dur != -1 ) ) GROUP BY group_id; -- Different device kernels log different actions when suspending. This table -- tells us the action that straddles the actual suspend period. DROP TABLE IF EXISTS device_action_mapping; CREATE TABLE device_action_mapping (device TEXT, action TEXT); INSERT INTO device_action_mapping VALUES ('blueline', 'timekeeping_freeze'), ('crosshatch', 'timekeeping_freeze'), ('bonito', 'timekeeping_freeze'), ('sargo', 'timekeeping_freeze'), ('coral', 'timekeeping_freeze'), ('flame', 'timekeeping_freeze'), ('sunfish', 'timekeeping_freeze'), ('redfin', 'syscore_resume'), ('bramble', 'syscore_resume'); DROP TABLE IF EXISTS device_action; CREATE TABLE device_action AS SELECT action FROM device_action_mapping dam WHERE EXISTS ( SELECT 1 FROM metadata WHERE name = 'android_build_fingerprint' AND str_value LIKE '%' || dam.device || '%'); DROP TABLE IF EXISTS suspend_slice_; CREATE TABLE suspend_slice_ AS -- Traces from after b/70292203 was fixed have the action string so just look -- for it. SELECT ts, dur, true as trustworthy FROM ( SELECT ts, LEAD(ts) OVER (ORDER BY ts, start DESC) - ts AS dur, start FROM ( SELECT ts, EXTRACT_ARG(arg_set_id, 'action') AS action, EXTRACT_ARG(arg_set_id, 'start') AS start FROM raw WHERE name = 'suspend_resume' ) JOIN device_action USING(action) ) WHERE start = 1 UNION ALL -- Traces from before b/70292203 was fixed (approx Nov 2020) do not have the -- action string so we do some convoluted pattern matching that mostly works. -- TODO(simonmacm) remove this when enough time has passed (mid 2021?) SELECT ts, dur, false as trustworthy FROM ( SELECT ts, ts - lag(ts) OVER w AS lag_dur, lead(ts) OVER w - ts AS dur, action, start, event, lag(start) OVER w AS lag_start, lag(event) OVER w AS lag_event, lag(start, 2) OVER w AS lag_2_start, lag(event, 2) OVER w AS lag_2_event, lead(start) OVER w AS lead_start, lead(event) OVER w AS lead_event, lead(start, 2) OVER w AS lead_2_start, lead(event, 2) OVER w AS lead_2_event FROM ( SELECT ts, EXTRACT_ARG(arg_set_id, 'action') AS action, EXTRACT_ARG(arg_set_id, 'start') AS start, EXTRACT_ARG(arg_set_id, 'val') AS event FROM raw WHERE name = 'suspend_resume' ) WINDOW w AS (ORDER BY ts) ) WHERE action IS NULL AND ( -- We want to find the start and end events with action='timekeeping_freeze'. -- In practice, these events often show up in a sequence like the following: -- start = 1, event = 1 [string would have been 'machine_suspend'] -- start = 1, event = (any) [string would have been 'timekeeping_freeze'] * -- -- (sleep happens here) -- -- start = 0, event = (any) [string would have been 'timekeeping_freeze'] -- start = 0, event = 1 [string would have been 'machine_suspend'] -- -- So we look for this pattern of start and event, anchored on the event marked -- with "*". ( lag_start = 1 AND lag_event = 1 AND start = 1 AND lead_start = 0 AND lead_2_start = 0 AND lead_2_event = 1 ) -- Or in newer kernels we seem to have a very different pattern. We can take -- advantage of that fact that we get several events with identical timestamp -- just before sleeping (normally this never happens): -- gap = 0, start = 1, event = 3 -- gap = 0, start = 0, event = 3 -- gap = 0, start = 1, event = 0 -- -- (sleep happens here) -- -- gap = (any), start = 0, event = 0 OR ( lag_dur = 0 AND lead_start = 0 AND lead_event = 0 AND start = 1 AND event = 0 AND lag_start = 0 AND lag_event = 3 AND lag_2_start = 1 AND lag_2_event = 3 ) ); DROP TABLE device_action_mapping; DROP TABLE device_action; SELECT RUN_METRIC('android/global_counter_span_view.sql', 'table_name', 'screen_state', 'counter_name', 'ScreenState'); DROP TABLE IF EXISTS screen_state_span_with_suspend; CREATE VIRTUAL TABLE screen_state_span_with_suspend USING span_join(screen_state_span, suspend_slice_); DROP VIEW IF EXISTS android_batt_event; CREATE VIEW android_batt_event AS SELECT ts, dur, 'Suspended' AS slice_name, 'Suspend / resume' AS track_name, 'slice' AS track_type FROM suspend_slice_; DROP VIEW IF EXISTS android_batt_output; CREATE VIEW android_batt_output AS SELECT AndroidBatteryMetric( 'battery_counters', ( SELECT RepeatedField( AndroidBatteryMetric_BatteryCounters( 'timestamp_ns', ts, 'charge_counter_uah', charge_uah, 'capacity_percent', capacity_percent, 'current_ua', current_ua, 'current_avg_ua', current_avg_ua ) ) FROM battery_view ), 'battery_aggregates', ( SELECT NULL_IF_EMPTY(AndroidBatteryMetric_BatteryAggregates( 'total_screen_off_ns', SUM(CASE WHEN state = 1.0 AND tbl = 'total' THEN dur ELSE 0 END), 'total_screen_on_ns', SUM(CASE WHEN state = 2.0 AND tbl = 'total' THEN dur ELSE 0 END), 'total_screen_doze_ns', SUM(CASE WHEN state = 3.0 AND tbl = 'total' THEN dur ELSE 0 END), 'sleep_ns', (SELECT SUM(dur) FROM suspend_slice_), 'sleep_screen_off_ns', SUM(CASE WHEN state = 1.0 AND tbl = 'sleep' THEN dur ELSE 0 END), 'sleep_screen_on_ns', SUM(CASE WHEN state = 2.0 AND tbl = 'sleep' THEN dur ELSE 0 END), 'sleep_screen_doze_ns', SUM(CASE WHEN state = 3.0 AND tbl = 'sleep' THEN dur ELSE 0 END), 'total_wakelock_ns', (SELECT SUM(ts_end - ts) FROM android_batt_wakelocks_merged) )) FROM ( SELECT dur, screen_state_val AS state, 'total' AS tbl FROM screen_state_span UNION ALL SELECT dur, screen_state_val AS state, 'sleep' AS tbl FROM screen_state_span_with_suspend ) ), 'suspend_period', ( SELECT RepeatedField( AndroidBatteryMetric_SuspendPeriod( 'timestamp_ns', ts, 'duration_ns', dur ) ) FROM suspend_slice_ WHERE trustworthy ) );