1 /*
2 * Copyright (C) 2019, 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 "utils.h"
18
19 #include "android-base/strings.h"
20
21 namespace android {
22 namespace stats_log_api_gen {
23
build_non_chained_decl_map(const Atoms & atoms,std::map<int,set<AtomDecl>::const_iterator> * decl_map)24 static void build_non_chained_decl_map(const Atoms& atoms,
25 std::map<int, set<AtomDecl>::const_iterator>* decl_map) {
26 for (set<AtomDecl>::const_iterator atom = atoms.non_chained_decls.begin();
27 atom != atoms.non_chained_decls.end(); atom++) {
28 decl_map->insert(std::make_pair(atom->code, atom));
29 }
30 }
31
32 /**
33 * Turn lower and camel case into upper case with underscores.
34 */
make_constant_name(const string & str)35 string make_constant_name(const string& str) {
36 string result;
37 const int N = str.size();
38 bool underscore_next = false;
39 for (int i=0; i<N; i++) {
40 char c = str[i];
41 if (c >= 'A' && c <= 'Z') {
42 if (underscore_next) {
43 result += '_';
44 underscore_next = false;
45 }
46 } else if (c >= 'a' && c <= 'z') {
47 c = 'A' + c - 'a';
48 underscore_next = true;
49 } else if (c == '_') {
50 underscore_next = false;
51 }
52 result += c;
53 }
54 return result;
55 }
56
cpp_type_name(java_type_t type)57 const char* cpp_type_name(java_type_t type) {
58 switch (type) {
59 case JAVA_TYPE_BOOLEAN:
60 return "bool";
61 case JAVA_TYPE_INT:
62 case JAVA_TYPE_ENUM:
63 return "int32_t";
64 case JAVA_TYPE_LONG:
65 return "int64_t";
66 case JAVA_TYPE_FLOAT:
67 return "float";
68 case JAVA_TYPE_DOUBLE:
69 return "double";
70 case JAVA_TYPE_STRING:
71 return "char const*";
72 case JAVA_TYPE_BYTE_ARRAY:
73 return "const BytesField&";
74 default:
75 return "UNKNOWN";
76 }
77 }
78
java_type_name(java_type_t type)79 const char* java_type_name(java_type_t type) {
80 switch (type) {
81 case JAVA_TYPE_BOOLEAN:
82 return "boolean";
83 case JAVA_TYPE_INT:
84 case JAVA_TYPE_ENUM:
85 return "int";
86 case JAVA_TYPE_LONG:
87 return "long";
88 case JAVA_TYPE_FLOAT:
89 return "float";
90 case JAVA_TYPE_DOUBLE:
91 return "double";
92 case JAVA_TYPE_STRING:
93 return "java.lang.String";
94 case JAVA_TYPE_BYTE_ARRAY:
95 return "byte[]";
96 default:
97 return "UNKNOWN";
98 }
99 }
100
atom_needed_for_module(const AtomDecl & atomDecl,const string & moduleName)101 bool atom_needed_for_module(const AtomDecl& atomDecl, const string& moduleName) {
102 if (moduleName == DEFAULT_MODULE_NAME) {
103 return true;
104 }
105 return atomDecl.hasModule && (moduleName == atomDecl.moduleName);
106 }
107
signature_needed_for_module(const set<string> & modules,const string & moduleName)108 bool signature_needed_for_module(const set<string>& modules, const string& moduleName) {
109 if (moduleName == DEFAULT_MODULE_NAME) {
110 return true;
111 }
112 return modules.find(moduleName) != modules.end();
113 }
114
115 // Native
116 // Writes namespaces for the cpp and header files, returning the number of namespaces written.
write_namespace(FILE * out,const string & cppNamespaces)117 void write_namespace(FILE* out, const string& cppNamespaces) {
118 vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
119 for (string cppNamespace : cppNamespaceVec) {
120 fprintf(out, "namespace %s {\n", cppNamespace.c_str());
121 }
122 }
123
124 // Writes namespace closing brackets for cpp and header files.
write_closing_namespace(FILE * out,const string & cppNamespaces)125 void write_closing_namespace(FILE* out, const string& cppNamespaces) {
126 vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
127 for (auto it = cppNamespaceVec.rbegin(); it != cppNamespaceVec.rend(); ++it) {
128 fprintf(out, "} // namespace %s\n", it->c_str());
129 }
130 }
131
write_cpp_usage(FILE * out,const string & method_name,const string & atom_code_name,const AtomDecl & atom,const AtomDecl & attributionDecl)132 static void write_cpp_usage(
133 FILE* out, const string& method_name, const string& atom_code_name,
134 const AtomDecl& atom, const AtomDecl &attributionDecl) {
135 fprintf(out, " * Usage: %s(StatsLog.%s", method_name.c_str(),
136 atom_code_name.c_str());
137
138 for (vector<AtomField>::const_iterator field = atom.fields.begin();
139 field != atom.fields.end(); field++) {
140 if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
141 for (auto chainField : attributionDecl.fields) {
142 if (chainField.javaType == JAVA_TYPE_STRING) {
143 fprintf(out, ", const std::vector<%s>& %s",
144 cpp_type_name(chainField.javaType),
145 chainField.name.c_str());
146 } else {
147 fprintf(out, ", const %s* %s, size_t %s_length",
148 cpp_type_name(chainField.javaType),
149 chainField.name.c_str(), chainField.name.c_str());
150 }
151 }
152 } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
153 fprintf(out, ", const std::map<int, int32_t>& %s_int"
154 ", const std::map<int, int64_t>& %s_long"
155 ", const std::map<int, char const*>& %s_str"
156 ", const std::map<int, float>& %s_float",
157 field->name.c_str(),
158 field->name.c_str(),
159 field->name.c_str(),
160 field->name.c_str());
161 } else {
162 fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
163 }
164 }
165 fprintf(out, ");\n");
166 }
167
write_native_atom_constants(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const string & moduleName)168 void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
169 const string& moduleName) {
170 fprintf(out, "/**\n");
171 fprintf(out, " * Constants for atom codes.\n");
172 fprintf(out, " */\n");
173 fprintf(out, "enum {\n");
174
175 std::map<int, set<AtomDecl>::const_iterator> atom_code_to_non_chained_decl_map;
176 build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
177
178 size_t i = 0;
179 // Print atom constants
180 for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
181 atom != atoms.decls.end(); atom++) {
182 // Skip if the atom is not needed for the module.
183 if (!atom_needed_for_module(*atom, moduleName)) {
184 continue;
185 }
186 string constant = make_constant_name(atom->name);
187 fprintf(out, "\n");
188 fprintf(out, " /**\n");
189 fprintf(out, " * %s %s\n", atom->message.c_str(), atom->name.c_str());
190 write_cpp_usage(out, "stats_write", constant, *atom, attributionDecl);
191
192 auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
193 if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
194 write_cpp_usage(out, "stats_write_non_chained", constant, *non_chained_decl->second,
195 attributionDecl);
196 }
197 fprintf(out, " */\n");
198 char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
199 fprintf(out, " %s = %d%s\n", constant.c_str(), atom->code, comma);
200 i++;
201 }
202 fprintf(out, "\n");
203 fprintf(out, "};\n");
204 fprintf(out, "\n");
205 }
206
write_native_method_signature(FILE * out,const string & methodName,const vector<java_type_t> & signature,const AtomDecl & attributionDecl,const string & closer)207 void write_native_method_signature(FILE* out, const string& methodName,
208 const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
209 const string& closer) {
210 fprintf(out, "%s(int32_t code", methodName.c_str());
211 int argIndex = 1;
212 for (vector<java_type_t>::const_iterator arg = signature.begin();
213 arg != signature.end(); arg++) {
214 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
215 for (auto chainField : attributionDecl.fields) {
216 if (chainField.javaType == JAVA_TYPE_STRING) {
217 fprintf(out, ", const std::vector<%s>& %s",
218 cpp_type_name(chainField.javaType),
219 chainField.name.c_str());
220 } else {
221 fprintf(out, ", const %s* %s, size_t %s_length",
222 cpp_type_name(chainField.javaType),
223 chainField.name.c_str(), chainField.name.c_str());
224 }
225 }
226 } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
227 fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
228 "const std::map<int, int64_t>& arg%d_2, "
229 "const std::map<int, char const*>& arg%d_3, "
230 "const std::map<int, float>& arg%d_4",
231 argIndex, argIndex, argIndex, argIndex);
232 } else {
233 fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
234 }
235 argIndex++;
236 }
237 fprintf(out, ")%s\n", closer.c_str());
238 }
239
write_native_method_call(FILE * out,const string & methodName,const vector<java_type_t> & signature,const AtomDecl & attributionDecl,int argIndex)240 void write_native_method_call(FILE* out, const string& methodName,
241 const vector<java_type_t>& signature, const AtomDecl& attributionDecl, int argIndex) {
242 fprintf(out, "%s(code", methodName.c_str());
243 for (vector<java_type_t>::const_iterator arg = signature.begin();
244 arg != signature.end(); arg++) {
245 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
246 for (auto chainField : attributionDecl.fields) {
247 if (chainField.javaType == JAVA_TYPE_STRING) {
248 fprintf(out, ", %s",
249 chainField.name.c_str());
250 } else {
251 fprintf(out, ", %s, %s_length",
252 chainField.name.c_str(), chainField.name.c_str());
253 }
254 }
255 } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
256 fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex,
257 argIndex, argIndex, argIndex);
258 } else {
259 fprintf(out, ", arg%d", argIndex);
260 }
261 argIndex++;
262 }
263 fprintf(out, ");\n");
264 }
265
266 // Java
write_java_atom_codes(FILE * out,const Atoms & atoms,const string & moduleName)267 void write_java_atom_codes(FILE* out, const Atoms& atoms, const string& moduleName) {
268 fprintf(out, " // Constants for atom codes.\n");
269
270 std::map<int, set<AtomDecl>::const_iterator> atom_code_to_non_chained_decl_map;
271 build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
272
273 // Print constants for the atom codes.
274 for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
275 atom != atoms.decls.end(); atom++) {
276 // Skip if the atom is not needed for the module.
277 if (!atom_needed_for_module(*atom, moduleName)) {
278 continue;
279 }
280 string constant = make_constant_name(atom->name);
281 fprintf(out, "\n");
282 fprintf(out, " /**\n");
283 fprintf(out, " * %s %s<br>\n", atom->message.c_str(), atom->name.c_str());
284 write_java_usage(out, "write", constant, *atom);
285 auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
286 if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
287 write_java_usage(out, "write_non_chained", constant, *non_chained_decl->second);
288 }
289 if (moduleName == DEFAULT_MODULE_NAME) {
290 fprintf(out, " * @hide\n");
291 }
292 fprintf(out, " */\n");
293 fprintf(out, " public static final int %s = %d;\n", constant.c_str(), atom->code);
294 }
295 fprintf(out, "\n");
296 }
297
write_java_enum_values(FILE * out,const Atoms & atoms,const string & moduleName)298 void write_java_enum_values(FILE* out, const Atoms& atoms, const string& moduleName) {
299 fprintf(out, " // Constants for enum values.\n\n");
300 for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
301 atom != atoms.decls.end(); atom++) {
302 // Skip if the atom is not needed for the module.
303 if (!atom_needed_for_module(*atom, moduleName)) {
304 continue;
305 }
306 for (vector<AtomField>::const_iterator field = atom->fields.begin();
307 field != atom->fields.end(); field++) {
308 if (field->javaType == JAVA_TYPE_ENUM) {
309 fprintf(out, " // Values for %s.%s\n", atom->message.c_str(),
310 field->name.c_str());
311 for (map<int, string>::const_iterator value = field->enumValues.begin();
312 value != field->enumValues.end(); value++) {
313 if (moduleName == DEFAULT_MODULE_NAME) {
314 fprintf(out, " /** @hide */\n");
315 }
316 fprintf(out, " public static final int %s__%s__%s = %d;\n",
317 make_constant_name(atom->message).c_str(),
318 make_constant_name(field->name).c_str(),
319 make_constant_name(value->second).c_str(),
320 value->first);
321 }
322 fprintf(out, "\n");
323 }
324 }
325 }
326 }
327
write_java_usage(FILE * out,const string & method_name,const string & atom_code_name,const AtomDecl & atom)328 void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
329 const AtomDecl& atom) {
330 fprintf(out, " * Usage: StatsLog.%s(StatsLog.%s",
331 method_name.c_str(), atom_code_name.c_str());
332 for (vector<AtomField>::const_iterator field = atom.fields.begin();
333 field != atom.fields.end(); field++) {
334 if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
335 fprintf(out, ", android.os.WorkSource workSource");
336 } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
337 fprintf(out, ", android.util.SparseArray<Object> value_map");
338 } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
339 fprintf(out, ", byte[] %s", field->name.c_str());
340 } else {
341 fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
342 }
343 }
344 fprintf(out, ");<br>\n");
345 }
346
write_java_non_chained_methods(FILE * out,const map<vector<java_type_t>,set<string>> & signatures_to_modules,const string & moduleName)347 int write_java_non_chained_methods(
348 FILE* out,
349 const map<vector<java_type_t>, set<string>>& signatures_to_modules,
350 const string& moduleName
351 ) {
352 for (auto signature_to_modules_it = signatures_to_modules.begin();
353 signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
354 // Skip if this signature is not needed for the module.
355 if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
356 continue;
357 }
358
359 // Print method signature.
360 if (DEFAULT_MODULE_NAME == moduleName) {
361 fprintf(out, " /** @hide */\n");
362 }
363 fprintf(out, " public static void write_non_chained(int code");
364 vector<java_type_t> signature = signature_to_modules_it->first;
365 int argIndex = 1;
366 for (vector<java_type_t>::const_iterator arg = signature.begin();
367 arg != signature.end(); arg++) {
368 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
369 // Non chained signatures should not have attribution chains.
370 return 1;
371 } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
372 // Module logging does not yet support key value pair.
373 return 1;
374 } else {
375 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
376 }
377 argIndex++;
378 }
379 fprintf(out, ") {\n");
380
381 fprintf(out, " write(code");
382 argIndex = 1;
383 for (vector<java_type_t>::const_iterator arg = signature.begin();
384 arg != signature.end(); arg++) {
385 // First two args are uid and tag of attribution chain.
386 if (argIndex == 1) {
387 fprintf(out, ", new int[] {arg%d}", argIndex);
388 } else if (argIndex == 2) {
389 fprintf(out, ", new java.lang.String[] {arg%d}", argIndex);
390 } else {
391 fprintf(out, ", arg%d", argIndex);
392 }
393 argIndex++;
394 }
395 fprintf(out, ");\n");
396 fprintf(out, " }\n");
397 fprintf(out, "\n");
398 }
399 return 0;
400 }
401
write_java_work_source_methods(FILE * out,const map<vector<java_type_t>,set<string>> & signatures_to_modules,const string & moduleName)402 int write_java_work_source_methods(
403 FILE* out,
404 const map<vector<java_type_t>, set<string>>& signatures_to_modules,
405 const string& moduleName
406 ) {
407 fprintf(out, " // WorkSource methods.\n");
408 for (auto signature_to_modules_it = signatures_to_modules.begin();
409 signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
410 // Skip if this signature is not needed for the module.
411 if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
412 continue;
413 }
414 vector<java_type_t> signature = signature_to_modules_it->first;
415 // Determine if there is Attribution in this signature.
416 int attributionArg = -1;
417 int argIndexMax = 0;
418 for (vector<java_type_t>::const_iterator arg = signature.begin();
419 arg != signature.end(); arg++) {
420 argIndexMax++;
421 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
422 if (attributionArg > -1) {
423 fprintf(stderr, "An atom contains multiple AttributionNode fields.\n");
424 fprintf(stderr, "This is not supported. Aborting WorkSource method writing.\n");
425 fprintf(out, "\n// Invalid for WorkSource: more than one attribution chain.\n");
426 return 1;
427 }
428 attributionArg = argIndexMax;
429 }
430 }
431 if (attributionArg < 0) {
432 continue;
433 }
434
435 fprintf(out, "\n");
436 // Method header (signature)
437 if (DEFAULT_MODULE_NAME == moduleName) {
438 fprintf(out, " /** @hide */\n");
439 }
440 fprintf(out, " public static void write(int code");
441 int argIndex = 1;
442 for (vector<java_type_t>::const_iterator arg = signature.begin();
443 arg != signature.end(); arg++) {
444 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
445 fprintf(out, ", android.os.WorkSource ws");
446 } else {
447 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
448 }
449 argIndex++;
450 }
451 fprintf(out, ") {\n");
452
453 // write_non_chained() component. TODO: Remove when flat uids are no longer needed.
454 fprintf(out, " for (int i = 0; i < ws.size(); ++i) {\n");
455 fprintf(out, " write_non_chained(code");
456 for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
457 if (argIndex == attributionArg) {
458 fprintf(out, ", ws.get(i), ws.getName(i)");
459 } else {
460 fprintf(out, ", arg%d", argIndex);
461 }
462 }
463 fprintf(out, ");\n");
464 fprintf(out, " }\n"); // close for-loop
465
466 // write() component.
467 fprintf(out, " java.util.ArrayList<android.os.WorkSource.WorkChain> workChains = "
468 "ws.getWorkChains();\n");
469 fprintf(out, " if (workChains != null) {\n");
470 fprintf(out, " for (android.os.WorkSource.WorkChain wc : workChains) {\n");
471 fprintf(out, " write(code");
472 for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
473 if (argIndex == attributionArg) {
474 fprintf(out, ", wc.getUids(), wc.getTags()");
475 } else {
476 fprintf(out, ", arg%d", argIndex);
477 }
478 }
479 fprintf(out, ");\n");
480 fprintf(out, " }\n"); // close for-loop
481 fprintf(out, " }\n"); // close if
482 fprintf(out, " }\n"); // close method
483 }
484 return 0;
485 }
486
487 } // namespace stats_log_api_gen
488 } // namespace android
489