1 /*
2 * Copyright (C) 2022 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/traced/probes/android_game_intervention_list/android_game_intervention_list_data_source.h"
18
19 #include <stddef.h>
20 #include <optional>
21
22 #include "perfetto/base/logging.h"
23 #include "perfetto/ext/base/scoped_file.h"
24 #include "perfetto/ext/base/string_splitter.h"
25 #include "perfetto/ext/base/string_utils.h"
26 #include "perfetto/ext/tracing/core/trace_writer.h"
27
28 #include "perfetto/tracing/core/data_source_config.h"
29 #include "protos/perfetto/config/android/android_game_intervention_list_config.pbzero.h"
30 #include "protos/perfetto/trace/android/android_game_intervention_list.pbzero.h"
31 #include "protos/perfetto/trace/trace_packet.pbzero.h"
32
33 namespace perfetto {
34
35 const char kAndroidGameInterventionListFileName[] =
36 "/data/system/game_mode_intervention.list";
37
38 // making the descriptor static
39 const ProbesDataSource::Descriptor
40 AndroidGameInterventionListDataSource::descriptor = {
41 /* name */ "android.game_interventions",
42 /* flags */ Descriptor::kFlagsNone,
43 /* fill_descriptor_func */ nullptr,
44 };
45
AndroidGameInterventionListDataSource(const DataSourceConfig & ds_config,TracingSessionID session_id,std::unique_ptr<TraceWriter> trace_writer)46 AndroidGameInterventionListDataSource::AndroidGameInterventionListDataSource(
47 const DataSourceConfig& ds_config,
48 TracingSessionID session_id,
49 std::unique_ptr<TraceWriter> trace_writer)
50 : ProbesDataSource(session_id, &descriptor),
51 trace_writer_(std::move(trace_writer)) {
52 perfetto::protos::pbzero::AndroidGameInterventionListConfig::Decoder cfg(
53 ds_config.android_game_intervention_list_config_raw());
54 for (auto name = cfg.package_name_filter(); name; ++name) {
55 package_name_filter_.emplace_back((*name).ToStdString());
56 }
57 }
58
59 AndroidGameInterventionListDataSource::
60 ~AndroidGameInterventionListDataSource() = default;
61
Start()62 void AndroidGameInterventionListDataSource::Start() {
63 auto trace_packet = trace_writer_->NewTracePacket();
64 auto* android_game_intervention_list_packet =
65 trace_packet->set_android_game_intervention_list();
66
67 base::ScopedFstream fs(fopen(kAndroidGameInterventionListFileName, "r"));
68 if (!fs) {
69 PERFETTO_ELOG("Failed to open %s", kAndroidGameInterventionListFileName);
70 android_game_intervention_list_packet->set_read_error(true);
71 } else {
72 bool is_parsed_fully = ParseAndroidGameInterventionListStream(
73 android_game_intervention_list_packet, fs, package_name_filter_);
74 if (!is_parsed_fully) {
75 android_game_intervention_list_packet->set_parse_error(true);
76 }
77 if (ferror(*fs)) {
78 android_game_intervention_list_packet->set_read_error(true);
79 }
80 }
81
82 trace_packet->Finalize();
83 trace_writer_->Flush();
84 }
85
Flush(FlushRequestID,std::function<void ()> callback)86 void AndroidGameInterventionListDataSource::Flush(
87 FlushRequestID,
88 std::function<void()> callback) {
89 callback();
90 }
91
92 bool AndroidGameInterventionListDataSource::
ParseAndroidGameInterventionListStream(protos::pbzero::AndroidGameInterventionList * packet,const base::ScopedFstream & fs,const std::vector<std::string> & package_name_filter)93 ParseAndroidGameInterventionListStream(
94 protos::pbzero::AndroidGameInterventionList* packet,
95 const base::ScopedFstream& fs,
96 const std::vector<std::string>& package_name_filter) {
97 bool is_parsed_fully = true;
98 char line_buf[2048];
99 while (fgets(line_buf, sizeof(line_buf), *fs) != nullptr) {
100 // removing trailing '\n'
101 // for processing fields with CStringTo* functions
102 line_buf[strlen(line_buf) - 1] = '\0';
103
104 if (!ParseAndroidGameInterventionListLine(line_buf, package_name_filter,
105 packet)) {
106 // marking parsed with error and continue with this line skipped
107 is_parsed_fully = false;
108 }
109 }
110 return is_parsed_fully;
111 }
112
113 bool AndroidGameInterventionListDataSource::
ParseAndroidGameInterventionListLine(char * line,const std::vector<std::string> & package_name_filter,protos::pbzero::AndroidGameInterventionList * packet)114 ParseAndroidGameInterventionListLine(
115 char* line,
116 const std::vector<std::string>& package_name_filter,
117 protos::pbzero::AndroidGameInterventionList* packet) {
118 size_t idx = 0;
119 perfetto::protos::pbzero::AndroidGameInterventionList_GamePackageInfo*
120 package = nullptr;
121 perfetto::protos::pbzero::AndroidGameInterventionList_GameModeInfo*
122 game_mode_info = nullptr;
123 for (base::StringSplitter string_splitter(line, '\t'); string_splitter.Next();
124 ++idx) {
125 // check if package name is in the name filter
126 // if not we skip parsing this line.
127 if (idx == 0) {
128 if (!package_name_filter.empty() &&
129 std::count(package_name_filter.begin(), package_name_filter.end(),
130 string_splitter.cur_token()) == 0) {
131 return true;
132 }
133 package = packet->add_game_packages();
134 }
135
136 switch (idx) {
137 case 0: {
138 package->set_name(string_splitter.cur_token(),
139 string_splitter.cur_token_size());
140 break;
141 }
142 case 1: {
143 std::optional<uint64_t> uid =
144 base::CStringToUInt64(string_splitter.cur_token());
145 if (uid == std::nullopt) {
146 PERFETTO_DLOG("Failed to parse game_mode_intervention.list uid.");
147 return false;
148 }
149 package->set_uid(uid.value());
150 break;
151 }
152 case 2: {
153 std::optional<uint32_t> cur_mode =
154 base::CStringToUInt32(string_splitter.cur_token());
155 if (cur_mode == std::nullopt) {
156 PERFETTO_DLOG(
157 "Failed to parse game_mode_intervention.list cur_mode.");
158 return false;
159 }
160 package->set_current_mode(cur_mode.value());
161 break;
162 }
163 case 3:
164 case 5:
165 case 7: {
166 std::optional<uint32_t> game_mode =
167 base::CStringToUInt32(string_splitter.cur_token());
168 if (game_mode == std::nullopt) {
169 PERFETTO_DLOG(
170 "Failed to parse game_mode_intervention.list game_mode.");
171 return false;
172 }
173 game_mode_info = package->add_game_mode_info();
174 game_mode_info->set_mode(game_mode.value());
175 break;
176 }
177 case 4:
178 case 6:
179 case 8: {
180 for (base::StringSplitter intervention_splitter(
181 string_splitter.cur_token(), ',');
182 intervention_splitter.Next();) {
183 base::StringSplitter value_splitter(intervention_splitter.cur_token(),
184 '=');
185 value_splitter.Next();
186 char* key = value_splitter.cur_token();
187 if (strcmp(key, "angle") == 0) {
188 value_splitter.Next();
189 std::optional<uint32_t> use_angle =
190 base::CStringToUInt32(value_splitter.cur_token());
191 if (use_angle == std::nullopt) {
192 PERFETTO_DLOG(
193 "Failed to parse game_mode_intervention.list use_angle.");
194 return false;
195 }
196 game_mode_info->set_use_angle(use_angle.value());
197 } else if (strcmp(key, "scaling") == 0) {
198 value_splitter.Next();
199 std::optional<double> resolution_downscale =
200 base::CStringToDouble(value_splitter.cur_token());
201 if (resolution_downscale == std::nullopt) {
202 PERFETTO_DLOG(
203 "Failed to parse game_mode_intervention.list "
204 "resolution_downscale.");
205 return false;
206 }
207 game_mode_info->set_resolution_downscale(
208 static_cast<float>(resolution_downscale.value()));
209 } else if (strcmp(key, "fps") == 0) {
210 value_splitter.Next();
211 std::optional<double> fps =
212 base::CStringToDouble(value_splitter.cur_token());
213 if (fps == std::nullopt) {
214 PERFETTO_DLOG("Failed to parse game_mode_intervention.list fps.");
215 return false;
216 }
217 game_mode_info->set_fps(static_cast<float>(fps.value()));
218 }
219 }
220 break;
221 }
222 }
223 }
224 return true;
225 }
226
227 } // namespace perfetto
228