1 /*
2  * Copyright (C) 2024 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  *      http://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 
17 #include "src/trace_processor/importers/android_bugreport/android_dumpstate_event_parser_impl.h"
18 
19 #include <cstdint>
20 #include <utility>
21 
22 #include "perfetto/base/status.h"
23 #include "perfetto/ext/base/no_destructor.h"
24 #include "perfetto/ext/base/status_or.h"
25 #include "perfetto/ext/base/string_splitter.h"
26 #include "perfetto/ext/base/string_utils.h"
27 #include "src/trace_processor/importers/android_bugreport/android_battery_stats_history_string_tracker.h"
28 #include "src/trace_processor/importers/android_bugreport/android_dumpstate_event.h"
29 #include "src/trace_processor/importers/common/async_track_set_tracker.h"
30 #include "src/trace_processor/importers/common/event_tracker.h"
31 #include "src/trace_processor/importers/common/process_tracker.h"
32 #include "src/trace_processor/importers/common/slice_tracker.h"
33 #include "src/trace_processor/importers/common/track_tracker.h"
34 #include "src/trace_processor/importers/common/tracks_common.h"
35 #include "src/trace_processor/importers/common/tracks_internal.h"
36 #include "src/trace_processor/tables/android_tables_py.h"
37 #include "src/trace_processor/types/trace_processor_context.h"
38 #include "src/trace_processor/util/status_macros.h"
39 
40 namespace perfetto::trace_processor {
41 
42 namespace {
GetEventFromShortName(base::StringView short_name)43 base::StatusOr<std::string> GetEventFromShortName(base::StringView short_name) {
44   static const base::NoDestructor<
45       std::unordered_map<base::StringView, std::string> >
46       checkin_event_name_to_enum(
47           std::unordered_map<base::StringView, std::string>({
48               {"Enl", "null"},       {"Epr", "proc"},
49               {"Efg", "fg"},         {"Etp", "top"},
50               {"Esy", "sync"},       {"Ewl", "wake_lock_in"},
51               {"Ejb", "job"},        {"Eur", "user"},
52               {"Euf", "userfg"},     {"Ecn", "conn"},
53               {"Eac", "active"},     {"Epi", "pkginst"},
54               {"Epu", "pkgunin"},    {"Eal", "alarm"},
55               {"Est", "stats"},      {"Eai", "pkginactive"},
56               {"Eaa", "pkgactive"},  {"Etw", "tmpwhitelist"},
57               {"Esw", "screenwake"}, {"Ewa", "wakeupap"},
58               {"Elw", "longwake"},   {"Eec", "est_capacity"},
59           }));
60   auto result = checkin_event_name_to_enum.ref().find(short_name);
61   if (result == checkin_event_name_to_enum.ref().end()) {
62     return base::ErrStatus("Failed to find historty event name mapping");
63   }
64   return result->second;
65 }
66 
67 struct StateStringTranslationInfo {
68   const std::string long_name;
69   const std::unordered_map<base::StringView, uint64_t> short_string_to_value;
70 };
71 
GetStateAndValueFromShortName(base::StringView state_short_name,base::StringView value_short_name,uint64_t * value_out)72 base::StatusOr<std::string> GetStateAndValueFromShortName(
73     base::StringView state_short_name,
74     base::StringView value_short_name,
75     uint64_t* value_out) {
76   // Mappings of all the state checkin names from BatteryStats.java and their
77   // corresponding value mappings
78   static const base::NoDestructor<
79       std::unordered_map<base::StringView, StateStringTranslationInfo> >
80       checkin_state_name_to_enum_and_values(
81           std::unordered_map<base::StringView, StateStringTranslationInfo>(
82               {{"r", {"running", {}}},
83                {"w", {"wake_lock", {}}},
84                {"s", {"sensor", {}}},
85                {"g", {"gps", {}}},
86                {"Wl", {"wifi_full_lock", {}}},
87                {"Ws", {"wifi_scan", {}}},
88                {"Wm", {"wifi_multicast", {}}},
89                {"Wr", {"wifi_radio", {}}},
90                {"Pr", {"mobile_radio", {}}},
91                {"Psc", {"phone_scanning", {}}},
92                {"a", {"audio", {}}},
93                {"S", {"screen", {}}},
94                {"BP", {"plugged", {}}},
95                {"Sd", {"screen_doze", {}}},
96                {"Pcn",
97                 {"data_conn",
98                  {
99                      {"oos", 0},     {"gprs", 1},    {"edge", 2},
100                      {"umts", 3},    {"cdma", 4},    {"evdo_0", 5},
101                      {"evdo_A", 6},  {"1xrtt", 7},   {"hsdpa", 8},
102                      {"hsupa", 9},   {"hspa", 10},   {"iden", 11},
103                      {"evdo_b", 12}, {"lte", 13},    {"ehrpd", 14},
104                      {"hspap", 15},  {"gsm", 16},    {"td_scdma", 17},
105                      {"iwlan", 18},  {"lte_ca", 19}, {"nr", 20},
106                      {"emngcy", 21}, {"other", 22},
107                  }}},
108                {"Pst",
109                 {"phone_state",
110                  {
111                      {"in", 0},
112                      {"out", 1},
113                      {"em", 2},
114                      {"off", 3},
115                  }}},
116                {"Pss", {"phone_signal_strength", {}}},
117                {"Sb", {"brightness", {}}},
118                {"ps", {"power_save", {}}},
119                {"v", {"video", {}}},
120                {"Ww", {"wifi_running", {}}},
121                {"W", {"wifi", {}}},
122                {"fl", {"flashlight", {}}},
123                {"di",
124                 {"device_idle",
125                  {
126                      {"off", 0},
127                      {"light", 1},
128                      {"full", 2},
129                      {"???", 3},
130                  }}},
131                {"ch", {"charging", {}}},
132                {"Ud", {"usb_data", {}}},
133                {"Pcl", {"phone_in_call", {}}},
134                {"b", {"bluetooth", {}}},
135                {"Wss", {"wifi_signal_strength", {}}},
136                {"Wsp",
137                 {"wifi_suppl",
138                  {
139                      {"inv", 0},
140                      {"dsc", 1},
141                      {"dis", 2},
142                      {"inact", 3},
143                      {"scan", 4},
144                      {"auth", 5},
145                      {"ascing", 6},
146                      {"asced", 7},
147                      {"4-way", 8},
148                      {"group", 9},
149                      {"compl", 10},
150                      {"dorm", 11},
151                      {"uninit", 12},
152                  }}},
153                {"ca", {"camera", {}}},
154                {"bles", {"ble_scan", {}}},
155                {"Chtp", {"cellular_high_tx_power", {}}},
156                {"Gss",
157                 {"gps_signal_quality",
158                  {
159                      {"poor", 0},
160                      {"good", 1},
161                      {"none", 2},
162                  }}},
163                {"nrs", {"nr_state", {}}}}));
164 
165   auto result =
166       checkin_state_name_to_enum_and_values.ref().find(state_short_name);
167   if (result == checkin_state_name_to_enum_and_values.ref().end()) {
168     return base::ErrStatus("Failed to find state short to long name mapping");
169   }
170 
171   StateStringTranslationInfo translation_info = result->second;
172 
173   // If caller isn't requesting a value, just return the item type.
174   if (value_out == nullptr) {
175     return translation_info.long_name;
176   }
177 
178   // If the value short name is already a number, just do a direct conversion
179   std::optional<uint64_t> possible_int_value =
180       base::StringToUInt64(value_short_name.ToStdString());
181   if (possible_int_value.has_value()) {
182     *value_out = possible_int_value.value();
183     return translation_info.long_name;
184   }
185   // value has a non-numerical string, so translate it
186   auto short_name_mapping =
187       translation_info.short_string_to_value.find(value_short_name);
188   if (short_name_mapping == translation_info.short_string_to_value.end()) {
189     return base::ErrStatus("Failed to translate value for state");
190   }
191   *value_out = short_name_mapping->second;
192   return translation_info.long_name;
193 }
194 
StringToStatusOrUInt64(base::StringView str)195 base::StatusOr<uint64_t> StringToStatusOrUInt64(base::StringView str) {
196   std::optional<uint64_t> possible_result =
197       base::StringToUInt64(str.ToStdString());
198   if (!possible_result.has_value()) {
199     return base::ErrStatus("Failed to convert string to uint64_t");
200   }
201   return possible_result.value();
202 }
203 
204 }  // namespace
205 
206 AndroidDumpstateEventParserImpl::~AndroidDumpstateEventParserImpl() = default;
207 
ParseAndroidDumpstateEvent(int64_t ts,AndroidDumpstateEvent event)208 void AndroidDumpstateEventParserImpl::ParseAndroidDumpstateEvent(
209     int64_t ts,
210     AndroidDumpstateEvent event) {
211   switch (event.type) {
212     case AndroidDumpstateEvent::EventType::kBatteryStatsHistoryEvent:
213       ProcessBatteryStatsHistoryItem(ts, event.raw_event);
214       return;
215     case AndroidDumpstateEvent::EventType::kNull:
216       return;
217   }
218 }
219 
ProcessBatteryStatsHistoryItem(int64_t ts,const std::string & raw_event)220 base::Status AndroidDumpstateEventParserImpl::ProcessBatteryStatsHistoryItem(
221     int64_t ts,
222     const std::string& raw_event) {
223   // TODO: migrate to future StringViewSplitter when availabile.
224   base::StringSplitter splitter(raw_event, '=');
225   base::StringView key =
226       base::StringView(splitter.Next() ? splitter.cur_token() : "");
227   base::StringView value =
228       base::StringView(splitter.Next() ? splitter.cur_token() : "");
229 
230   AndroidBatteryStatsHistoryStringTracker* history_string_tracker =
231       AndroidBatteryStatsHistoryStringTracker::GetOrCreate(context_);
232 
233   std::string item_name;
234   if (key.StartsWith("+E") || key.StartsWith("-E") || key.StartsWith("E")) {
235     // Process a history event
236     base::StringView prefix = "";
237     if (key.at(0) == '+' || key.at(0) == '-') {
238       prefix = key.substr(0, 1);
239       key = key.substr(1);
240     }
241     ASSIGN_OR_RETURN(item_name, GetEventFromShortName(key));
242     ASSIGN_OR_RETURN(uint64_t hsp_index, StringToStatusOrUInt64(value));
243     const int32_t uid = history_string_tracker->GetUid(hsp_index);
244     const std::string& event_str = history_string_tracker->GetString(hsp_index);
245     StringId track_name_id = context_->storage->InternString(
246         std::string("battery_stats.").append(item_name));
247     const std::string slice_name = prefix.ToStdString()
248                                        .append(item_name)
249                                        .append("=")
250                                        .append(std::to_string(uid))
251                                        .append(":\"")
252                                        .append(event_str)
253                                        .append("\"");
254     StringId name_id = context_->storage->InternString(slice_name);
255     AsyncTrackSetTracker::TrackSetId track_set_id =
256         context_->async_track_set_tracker->InternGlobalTrackSet(track_name_id);
257     TrackId track_id =
258         context_->async_track_set_tracker->Scoped(track_set_id, ts, 0);
259     context_->slice_tracker->Scoped(ts, track_id, kNullStringId, name_id, 0);
260     return base::OkStatus();
261   } else if ((key.StartsWith("+") || key.StartsWith("-")) && value.empty()) {
262     // Process a history state of the form "+state" or "-state"
263 
264     // To match behavior of the battery stats atrace implementation, avoid
265     // including Wakelock events in the trace
266     if (key == "+w" || key == "-w") {
267       return base::OkStatus();
268     }
269 
270     ASSIGN_OR_RETURN(item_name,
271                      GetStateAndValueFromShortName(key.substr(1), "", nullptr));
272     TrackId track = context_->track_tracker->InternTrack(
273         tracks::kAndroidBatteryStatsBlueprint,
274         tracks::Dimensions(
275             base::StringView(std::string("battery_stats.").append(item_name))));
276     context_->event_tracker->PushCounter(ts, (key.at(0) == '+') ? 1.0 : 0.0,
277                                          track);
278     // Also add a screen events to the screen state track
279     if (item_name == "screen") {
280       track = context_->track_tracker->InternTrack(
281           tracks::kAndroidScreenStateBlueprint);
282       // battery_stats.screen event is 0 for off and 1 for on, but the
283       // ScreenState track uses the convention 1 for off and 2 for on, so add
284       // 1 to the current counter value.
285       context_->event_tracker->PushCounter(
286           ts, static_cast<double>((key.at(0) == '+') ? 2.0 : 1.0), track);
287     }
288     return base::OkStatus();
289   } else if (!key.StartsWith("+") && !key.StartsWith("-") && !value.empty()) {
290     // AndroidProbesParser will use the empty string for the battery name if no
291     // battery name is associated with the data, which is common on most pixel
292     // phones. Adopt the same convention here. Battery stats does not provide
293     // a battery name in the checking format, so we'll always have an unknown
294     // battery.
295     const base::StringView kUnknownBatteryName = "";
296 
297     // process history state of form "state=12345" or "state=abcde"
298     TrackId counter_track;
299     uint64_t counter_value;
300     base::StatusOr<std::string> possible_history_state_item =
301         GetStateAndValueFromShortName(key, value, &counter_value);
302     if (possible_history_state_item.ok()) {
303       item_name = possible_history_state_item.value();
304       counter_track = context_->track_tracker->InternTrack(
305           tracks::kAndroidBatteryStatsBlueprint,
306           tracks::Dimensions(base::StringView(
307               std::string("battery_stats.").append(item_name))));
308     } else if (key == "Bl") {
309       counter_track = context_->track_tracker->InternTrack(
310           tracks::kBatteryCounterBlueprint,
311           tracks::Dimensions(kUnknownBatteryName, "capacity_pct"));
312       ASSIGN_OR_RETURN(counter_value, StringToStatusOrUInt64(value));
313     } else if (key == "Bcc") {
314       counter_track = context_->track_tracker->InternTrack(
315           tracks::kBatteryCounterBlueprint,
316           tracks::Dimensions(kUnknownBatteryName, "charge_uah"));
317       ASSIGN_OR_RETURN(counter_value, StringToStatusOrUInt64(value));
318       // battery stats gives us charge in milli-amp-hours, but the track
319       // expects the value to be in micro-amp-hours
320       counter_value *= 1000;
321     } else if (key == "Bv") {
322       counter_track = context_->track_tracker->InternTrack(
323           tracks::kBatteryCounterBlueprint,
324           tracks::Dimensions(kUnknownBatteryName, "voltage_uv"));
325       ASSIGN_OR_RETURN(counter_value, StringToStatusOrUInt64(value));
326       // battery stats gives us charge in milli-volts, but the track
327       // expects the value to be in micro-volts
328       counter_value *= 1000;
329     } else if (key == "Bs") {
330       static constexpr auto kBatteryStatusBlueprint = tracks::CounterBlueprint(
331           "battery_status", tracks::UnknownUnitBlueprint(),
332           tracks::DimensionBlueprints(),
333           tracks::StaticNameBlueprint("BatteryStatus"));
334       counter_track =
335           context_->track_tracker->InternTrack(kBatteryStatusBlueprint);
336       switch (value.at(0)) {
337         case '?':
338           counter_value = 1;  // BatteryManager.BATTERY_STATUS_UNKNOWN
339           break;
340         case 'c':
341           counter_value = 2;  // BatteryManager.BATTERY_STATUS_CHARGING
342           break;
343         case 'd':
344           counter_value = 3;  // BatteryManager.BATTERY_STATUS_DISCHARGING
345           break;
346         case 'n':
347           counter_value = 4;  // BatteryManager.BATTERY_STATUS_NOT_CHARGING
348           break;
349         case 'f':
350           counter_value = 5;  // BatteryManager.BATTERY_STATUS_FULL
351           break;
352         default:
353           PERFETTO_ELOG("unknown battery status: %c", value.at(0));
354           counter_value = 0;  // not a valid enum
355       }
356     } else if (key == "Bp") {
357       static constexpr auto kPluggedStatusBluePrint = tracks::CounterBlueprint(
358           "battery_plugged_status", tracks::UnknownUnitBlueprint(),
359           tracks::DimensionBlueprints(),
360           tracks::StaticNameBlueprint("PlugType"));
361       counter_track =
362           context_->track_tracker->InternTrack(kPluggedStatusBluePrint);
363       switch (value.at(0)) {
364         case 'n':
365           counter_value = 0;  // BatteryManager.BATTERY_PLUGGED_NONE
366           break;
367         case 'a':
368           counter_value = 1;  // BatteryManager.BATTERY_PLUGGED_AC
369           break;
370         case 'u':
371           counter_value = 2;  // BatteryManager.BATTERY_PLUGGED_USB
372           break;
373         case 'w':
374           counter_value = 4;  // BatteryManager.BATTERY_PLUGGED_WIRELESS
375           break;
376         default:
377           counter_value = 0;  // BatteryManager.BATTERY_PLUGGED_NONE
378       }
379     } else {
380       return base::ErrStatus("Unhandled event");
381     }
382 
383     context_->event_tracker->PushCounter(ts, static_cast<double>(counter_value),
384                                          counter_track);
385     return base::OkStatus();
386   } else {
387     return base::ErrStatus("Unhandled event");
388   }
389 }
390 
391 }  // namespace perfetto::trace_processor
392