1-- 2-- Copyright 2023 The Android Open Source Project 3-- 4-- Licensed under the Apache License, Version 2.0 (the "License"); 5-- you may not use this file except in compliance with the License. 6-- You may obtain a copy of the License at 7-- 8-- https://www.apache.org/licenses/LICENSE-2.0 9-- 10-- Unless required by applicable law or agreed to in writing, software 11-- distributed under the License is distributed on an "AS IS" BASIS, 12-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13-- See the License for the specific language governing permissions and 14-- limitations under the License. 15 16-- Converts a battery_stats counter value to human readable string. 17CREATE PERFETTO FUNCTION android_battery_stats_counter_to_string( 18 -- The counter track name (e.g. 'battery_stats.audio'). 19 track STRING, 20 -- The counter value. 21 value FLOAT) 22-- The human-readable name for the counter value. 23RETURNS STRING AS 24SELECT 25 CASE 26 WHEN ($track = "battery_stats.wifi_scan" OR 27 $track = "battery_stats.wifi_radio" OR 28 $track = "battery_stats.mobile_radio" OR 29 $track = "battery_stats.audio" OR 30 $track = "battery_stats.video" OR 31 $track = "battery_stats.camera" OR 32 $track = "battery_stats.power_save" OR 33 $track = "battery_stats.phone_in_call") 34 THEN 35 CASE $value 36 WHEN 0 THEN "inactive" 37 WHEN 1 THEN "active" 38 ELSE "unknown" 39 END 40 WHEN $track = "battery_stats.wifi" 41 THEN 42 CASE $value 43 WHEN 0 THEN "off" 44 WHEN 1 THEN "on" 45 ELSE "unknown" 46 END 47 WHEN $track = "battery_stats.phone_state" 48 THEN 49 CASE $value 50 WHEN 0 THEN "in" 51 WHEN 1 THEN "out" 52 WHEN 2 THEN "emergency" 53 WHEN 3 THEN "off" 54 ELSE "unknown" 55 END 56 WHEN ($track = "battery_stats.phone_signal_strength" OR 57 $track = "battery_stats.wifi_signal_strength") 58 THEN 59 CASE $value 60 WHEN 0 THEN "0/4" 61 WHEN 1 THEN "1/4" 62 WHEN 2 THEN "2/4" 63 WHEN 3 THEN "3/4" 64 WHEN 4 THEN "4/4" 65 ELSE "unknown" 66 END 67 WHEN $track = "battery_stats.wifi_suppl" 68 THEN 69 CASE $value 70 WHEN 0 THEN "invalid" 71 WHEN 1 THEN "disconnected" 72 WHEN 2 THEN "disabled" 73 WHEN 3 THEN "inactive" 74 WHEN 4 THEN "scanning" 75 WHEN 5 THEN "authenticating" 76 WHEN 6 THEN "associating" 77 WHEN 7 THEN "associated" 78 WHEN 8 THEN "4-way-handshake" 79 WHEN 9 THEN "group-handshake" 80 WHEN 10 THEN "completed" 81 WHEN 11 THEN "dormant" 82 WHEN 12 THEN "uninitialized" 83 ELSE "unknown" 84 END 85 WHEN $track = "battery_stats.data_conn" 86 THEN 87 CASE $value 88 WHEN 0 THEN "Out of service" 89 WHEN 1 THEN "2.5G (GPRS)" 90 WHEN 2 THEN "2.7G (EDGE)" 91 WHEN 3 THEN "3G (UMTS)" 92 WHEN 4 THEN "3G (CDMA)" 93 WHEN 5 THEN "3G (EVDO Rel 0)" 94 WHEN 6 THEN "3G (EVDO Rev A)" 95 WHEN 7 THEN "3G (LXRTT)" 96 WHEN 8 THEN "3.5G (HSDPA)" 97 WHEN 9 THEN "3.5G (HSUPA)" 98 WHEN 10 THEN "3.5G (HSPA)" 99 WHEN 11 THEN "2G (IDEN)" 100 WHEN 12 THEN "3G (EVDO Rev B)" 101 WHEN 13 THEN "4G (LTE)" 102 WHEN 14 THEN "3.5G (eHRPD)" 103 WHEN 15 THEN "3.7G (HSPA+)" 104 WHEN 16 THEN "2G (GSM)" 105 WHEN 17 THEN "3G (TD SCDMA)" 106 WHEN 18 THEN "Wifi calling (IWLAN)" 107 WHEN 19 THEN "4.5G (LTE CA)" 108 WHEN 20 THEN "5G (NR)" 109 WHEN 21 THEN "Emergency calls only" 110 WHEN 22 THEN "Other" 111 ELSE "unknown" 112 END 113 ELSE CAST($value AS text) 114 END; 115 116-- View of human readable battery stats counter-based states. These are recorded 117-- by BatteryStats as a bitmap where each 'category' has a unique value at any 118-- given time. 119CREATE PERFETTO VIEW android_battery_stats_state( 120 -- Timestamp in nanoseconds. 121 ts INT, 122 -- The duration the state was active, may be negative for incomplete slices. 123 dur INT, 124 -- The same as `dur`, but extends to trace end for incomplete slices. 125 safe_dur INT, 126 -- The name of the counter track. 127 track_name STRING, 128 -- The counter value as a number. 129 value INT, 130 -- The counter value as a human-readable string. 131 value_name STRING 132) AS 133SELECT 134 ts, 135 IFNULL(LEAD(ts) OVER (PARTITION BY name ORDER BY ts) - ts, -1) AS dur, 136 LEAD(ts, 1, TRACE_END()) OVER (PARTITION BY name ORDER BY ts) - ts AS safe_dur, 137 name AS track_name, 138 CAST(value AS INT64) AS value, 139 android_battery_stats_counter_to_string(name, value) AS value_name 140FROM counter 141JOIN counter_track 142 ON counter.track_id = counter_track.id 143WHERE counter_track.name GLOB 'battery_stats.*'; 144 145 146-- View of slices derived from battery_stats events. Battery stats records all 147-- events as instants, however some may indicate whether something started or 148-- stopped with a '+' or '-' prefix. Events such as jobs, top apps, foreground 149-- apps or long wakes include these details and allow drawing slices between 150-- instant events found in a trace. 151-- 152-- For example, we may see an event like the following on 'battery_stats.top': 153-- 154-- -top=10215:"com.google.android.apps.nexuslauncher" 155-- 156-- This view will find the associated start ('+top') with the matching suffix 157-- (everything after the '=') to construct a slice. It computes the timestamp 158-- and duration from the events and extract the details as follows: 159-- 160-- track_name='battery_stats.top' 161-- str_value='com.google.android.apps.nexuslauncher' 162-- int_value=10215 163CREATE PERFETTO VIEW android_battery_stats_event_slices( 164 -- Timestamp in nanoseconds. 165 ts INT, 166 -- The duration the state was active, may be negative for incomplete slices. 167 dur INT, 168 -- The same as `dur`, but extends to trace end for incomplete slices. 169 safe_dur INT, 170 -- The name of the counter track. 171 track_name STRING, 172 -- String value. 173 str_value STRING, 174 -- Int value. 175 int_value INT 176) AS 177WITH 178 event_markers AS ( 179 SELECT 180 ts, 181 track.name AS track_name, 182 str_split(slice.name, '=', 1) AS key, 183 substr(slice.name, 1, 1) = '+' AS start 184 FROM slice 185 JOIN track 186 ON slice.track_id = track.id 187 WHERE 188 track_name GLOB 'battery_stats.*' 189 AND substr(slice.name, 1, 1) IN ('+', '-') 190 ), 191 with_neighbors AS ( 192 SELECT 193 *, 194 LAG(ts) OVER (PARTITION BY track_name, key ORDER BY ts) AS last_ts, 195 LEAD(ts) OVER (PARTITION BY track_name, key ORDER BY ts) AS next_ts 196 FROM event_markers 197 ), 198 -- Note: query performance depends on the ability to push down filters on 199 -- the track_name. It would be more clear below to have two queries and union 200 -- them, but doing so prevents push down through the above window functions. 201 event_spans AS ( 202 SELECT 203 track_name, key, 204 IIF(start, ts, trace_start()) AS ts, 205 IIF(start, next_ts, ts) AS end_ts 206 FROM with_neighbors 207 -- For the majority of events, we take the `start` event and compute the dur 208 -- based on next_ts. In the off chance we get an end event with no prior 209 -- start (matched by the second half of this where), we can create an event 210 -- starting from the beginning of the trace ending at the current event. 211 WHERE (start OR last_ts IS NULL) 212 ) 213SELECT 214 ts, 215 IFNULL(end_ts-ts, -1) AS dur, 216 IFNULL(end_ts, TRACE_END()) - ts AS safe_dur, 217 track_name, 218 str_split(key, '"', 1) AS str_value, 219 CAST(str_split(key, ':', 0) AS INT64) AS int_value 220FROM event_spans; 221