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 "native_writer.h"
18
19 #include <stdio.h>
20
21 #include "Collation.h"
22 #include "utils.h"
23
24 namespace android {
25 namespace stats_log_api_gen {
26
write_native_annotation_constants(FILE * out)27 static void write_native_annotation_constants(FILE* out) {
28 fprintf(out, "// Annotation constants.\n");
29
30 const map<AnnotationId, AnnotationStruct>& ANNOTATION_ID_CONSTANTS =
31 get_annotation_id_constants(ANNOTATION_CONSTANT_NAME_PREFIX);
32 for (const auto& [id, annotation] : ANNOTATION_ID_CONSTANTS) {
33 fprintf(out, "const uint8_t %s = %hhu;\n", annotation.name.c_str(), id);
34 }
35 fprintf(out, "\n");
36 }
37
write_annotations(FILE * out,int argIndex,const FieldNumberToAtomDeclSet & fieldNumberToAtomDeclSet,const string & methodPrefix,const string & methodSuffix,const int minApiLevel)38 static void write_annotations(FILE* out, int argIndex,
39 const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet,
40 const string& methodPrefix, const string& methodSuffix,
41 const int minApiLevel) {
42 const FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
43 fieldNumberToAtomDeclSet.find(argIndex);
44 if (fieldNumberToAtomDeclSet.end() == fieldNumberToAtomDeclSetIt) {
45 return;
46 }
47 const AtomDeclSet& atomDeclSet = fieldNumberToAtomDeclSetIt->second;
48 const map<AnnotationId, AnnotationStruct>& ANNOTATION_ID_CONSTANTS =
49 get_annotation_id_constants(ANNOTATION_CONSTANT_NAME_PREFIX);
50 const string constantPrefix = minApiLevel > API_R ? "ASTATSLOG_" : "";
51 for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
52 const string atomConstant = make_constant_name(atomDecl->name);
53 fprintf(out, " if (%s == code) {\n", atomConstant.c_str());
54 const AnnotationSet& annotations = atomDecl->fieldNumberToAnnotations.at(argIndex);
55 int resetState = -1;
56 int defaultState = -1;
57 for (const shared_ptr<Annotation>& annotation : annotations) {
58 const string& annotationConstant =
59 ANNOTATION_ID_CONSTANTS.at(annotation->annotationId).name;
60 switch (annotation->type) {
61 case ANNOTATION_TYPE_INT:
62 if (ANNOTATION_ID_TRIGGER_STATE_RESET == annotation->annotationId) {
63 resetState = annotation->value.intValue;
64 } else if (ANNOTATION_ID_DEFAULT_STATE == annotation->annotationId) {
65 defaultState = annotation->value.intValue;
66 } else if (ANNOTATION_ID_RESTRICTION_CATEGORY == annotation->annotationId) {
67 fprintf(out, " %saddInt32Annotation(%s%s%s,\n",
68 methodPrefix.c_str(), methodSuffix.c_str(), constantPrefix.c_str(),
69 annotationConstant.c_str());
70 fprintf(out, " %s%s);\n",
71 constantPrefix.c_str(),
72 get_restriction_category_str(annotation->value.intValue).c_str());
73 } else {
74 fprintf(out, " %saddInt32Annotation(%s%s%s, %d);\n",
75 methodPrefix.c_str(), methodSuffix.c_str(), constantPrefix.c_str(),
76 annotationConstant.c_str(), annotation->value.intValue);
77 }
78 break;
79 case ANNOTATION_TYPE_BOOL:
80 fprintf(out, " %saddBoolAnnotation(%s%s%s, %s);\n", methodPrefix.c_str(),
81 methodSuffix.c_str(), constantPrefix.c_str(),
82 annotationConstant.c_str(),
83 annotation->value.boolValue ? "true" : "false");
84 break;
85 default:
86 break;
87 }
88 }
89 if (defaultState != -1 && resetState != -1) {
90 const string& annotationConstant =
91 ANNOTATION_ID_CONSTANTS.at(ANNOTATION_ID_TRIGGER_STATE_RESET).name;
92 fprintf(out, " if (arg%d == %d) {\n", argIndex, resetState);
93 fprintf(out, " %saddInt32Annotation(%s%s%s, %d);\n", methodPrefix.c_str(),
94 methodSuffix.c_str(), constantPrefix.c_str(), annotationConstant.c_str(),
95 defaultState);
96 fprintf(out, " }\n");
97 }
98 fprintf(out, " }\n");
99 }
100 }
101
write_native_method_body(FILE * out,const vector<java_type_t> & signature,const FieldNumberToAtomDeclSet & fieldNumberToAtomDeclSet,const AtomDecl & attributionDecl,const int minApiLevel)102 static int write_native_method_body(FILE* out, const vector<java_type_t>& signature,
103 const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet,
104 const AtomDecl& attributionDecl, const int minApiLevel) {
105 int argIndex = 1;
106 fprintf(out, " AStatsEvent_setAtomId(event, code);\n");
107 write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "AStatsEvent_",
108 "event, ", minApiLevel);
109 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
110 arg++) {
111 if (minApiLevel < API_T && is_repeated_field(*arg)) {
112 fprintf(stderr, "Found repeated field type with min api level < T.");
113 return 1;
114 }
115 switch (*arg) {
116 case JAVA_TYPE_ATTRIBUTION_CHAIN: {
117 const char* uidName = attributionDecl.fields.front().name.c_str();
118 const char* tagName = attributionDecl.fields.back().name.c_str();
119 fprintf(out,
120 " AStatsEvent_writeAttributionChain(event, "
121 "reinterpret_cast<const uint32_t*>(%s), %s.data(), "
122 "static_cast<uint8_t>(%s_length));\n",
123 uidName, tagName, uidName);
124 break;
125 }
126 case JAVA_TYPE_BYTE_ARRAY:
127 fprintf(out,
128 " AStatsEvent_writeByteArray(event, "
129 "reinterpret_cast<const uint8_t*>(arg%d.arg), "
130 "arg%d.arg_length);\n",
131 argIndex, argIndex);
132 break;
133 case JAVA_TYPE_BOOLEAN:
134 fprintf(out, " AStatsEvent_writeBool(event, arg%d);\n", argIndex);
135 break;
136 case JAVA_TYPE_INT:
137 [[fallthrough]];
138 case JAVA_TYPE_ENUM:
139 fprintf(out, " AStatsEvent_writeInt32(event, arg%d);\n", argIndex);
140 break;
141 case JAVA_TYPE_FLOAT:
142 fprintf(out, " AStatsEvent_writeFloat(event, arg%d);\n", argIndex);
143 break;
144 case JAVA_TYPE_LONG:
145 fprintf(out, " AStatsEvent_writeInt64(event, arg%d);\n", argIndex);
146 break;
147 case JAVA_TYPE_STRING:
148 fprintf(out, " AStatsEvent_writeString(event, arg%d);\n", argIndex);
149 break;
150 case JAVA_TYPE_BOOLEAN_ARRAY:
151 fprintf(out, " AStatsEvent_writeBoolArray(event, arg%d, arg%d_length);\n",
152 argIndex, argIndex);
153 break;
154 case JAVA_TYPE_INT_ARRAY:
155 [[fallthrough]];
156 case JAVA_TYPE_ENUM_ARRAY:
157 fprintf(out,
158 " AStatsEvent_writeInt32Array(event, arg%d.data(), arg%d.size());\n",
159 argIndex, argIndex);
160 break;
161 case JAVA_TYPE_FLOAT_ARRAY:
162 fprintf(out,
163 " AStatsEvent_writeFloatArray(event, arg%d.data(), arg%d.size());\n",
164 argIndex, argIndex);
165 break;
166 case JAVA_TYPE_LONG_ARRAY:
167 fprintf(out,
168 " AStatsEvent_writeInt64Array(event, arg%d.data(), arg%d.size());\n",
169 argIndex, argIndex);
170 break;
171 case JAVA_TYPE_STRING_ARRAY:
172 fprintf(out,
173 " AStatsEvent_writeStringArray(event, arg%d.data(), arg%d.size());\n",
174 argIndex, argIndex);
175 break;
176
177 default:
178 // Unsupported types: OBJECT, DOUBLE
179 fprintf(stderr, "Encountered unsupported type.\n");
180 return 1;
181 }
182 write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "AStatsEvent_", "event, ",
183 minApiLevel);
184 argIndex++;
185 }
186 return 0;
187 }
188
write_native_method_call(FILE * out,const string & methodName,const vector<java_type_t> & signature,const AtomDecl & attributionDecl,int argIndex)189 static void write_native_method_call(FILE* out, const string& methodName,
190 const vector<java_type_t>& signature,
191 const AtomDecl& attributionDecl, int argIndex) {
192 fprintf(out, "%s(code", methodName.c_str());
193 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
194 arg++) {
195 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
196 for (const auto& chainField : attributionDecl.fields) {
197 if (chainField.javaType == JAVA_TYPE_STRING) {
198 fprintf(out, ", %s", chainField.name.c_str());
199 } else {
200 fprintf(out, ", %s, %s_length", chainField.name.c_str(),
201 chainField.name.c_str());
202 }
203 }
204 } else {
205 fprintf(out, ", arg%d", argIndex);
206
207 if (*arg == JAVA_TYPE_BOOLEAN_ARRAY) {
208 fprintf(out, ", arg%d_length", argIndex);
209 }
210 }
211 argIndex++;
212 }
213 fprintf(out, ");\n");
214 }
215
write_native_stats_write_methods(FILE * out,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl,const int minApiLevel,bool bootstrap)216 static int write_native_stats_write_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
217 const AtomDecl& attributionDecl, const int minApiLevel,
218 bool bootstrap) {
219 fprintf(out, "\n");
220 for (const auto& [signature, fieldNumberToAtomDeclSet] : signatureInfoMap) {
221 write_native_method_signature(out, "int stats_write(", signature, attributionDecl, " {");
222
223 // Write method body.
224 if (bootstrap) {
225 fprintf(out, " ::android::os::StatsBootstrapAtom atom;\n");
226 fprintf(out, " atom.atomId = code;\n");
227 const FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
228 fieldNumberToAtomDeclSet.find(ATOM_ID_FIELD_NUMBER);
229 if (fieldNumberToAtomDeclSet.end() != fieldNumberToAtomDeclSetIt) {
230 fprintf(stderr, "Bootstrap atoms do not support annotations\n");
231 return 1;
232 }
233 int argIndex = 1;
234 const char* atomVal = "::android::os::StatsBootstrapAtomValue::";
235 for (vector<java_type_t>::const_iterator arg = signature.begin();
236 arg != signature.end(); arg++) {
237 switch (*arg) {
238 case JAVA_TYPE_BYTE_ARRAY:
239 fprintf(out,
240 " const uint8_t* arg%dbyte = reinterpret_cast<const "
241 "uint8_t*>(arg%d.arg);\n",
242 argIndex, argIndex);
243 fprintf(out,
244 " "
245 "atom.values.push_back(%smake<%sbytesValue>(std::vector(arg%dbyte, "
246 "arg%dbyte + arg%d.arg_length)));\n",
247 atomVal, atomVal, argIndex, argIndex, argIndex);
248 break;
249 case JAVA_TYPE_BOOLEAN:
250 fprintf(out, " atom.values.push_back(%smake<%sboolValue>(arg%d));\n",
251 atomVal, atomVal, argIndex);
252 break;
253 case JAVA_TYPE_INT: // Fall through.
254 case JAVA_TYPE_ENUM:
255 fprintf(out, " atom.values.push_back(%smake<%sintValue>(arg%d));\n",
256 atomVal, atomVal, argIndex);
257 break;
258 case JAVA_TYPE_FLOAT:
259 fprintf(out, " atom.values.push_back(%smake<%sfloatValue>(arg%d));\n",
260 atomVal, atomVal, argIndex);
261 break;
262 case JAVA_TYPE_LONG:
263 fprintf(out, " atom.values.push_back(%smake<%slongValue>(arg%d));\n",
264 atomVal, atomVal, argIndex);
265 break;
266 case JAVA_TYPE_STRING:
267 fprintf(out,
268 " atom.values.push_back(%smake<%sstringValue>("
269 "::android::String16(arg%d)));\n",
270 atomVal, atomVal, argIndex);
271 break;
272 default:
273 // Unsupported types: OBJECT, DOUBLE, ATTRIBUTION_CHAIN,
274 // and all repeated fields
275 fprintf(stderr, "Encountered unsupported type.\n");
276 return 1;
277 }
278 const FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt =
279 fieldNumberToAtomDeclSet.find(argIndex);
280 if (fieldNumberToAtomDeclSet.end() != fieldNumberToAtomDeclSetIt) {
281 fprintf(stderr, "Bootstrap atoms do not support annotations\n");
282 return 1;
283 }
284 argIndex++;
285 }
286 fprintf(out,
287 " bool success = "
288 "::android::os::stats::StatsBootstrapAtomClient::reportBootstrapAtom(atom);\n");
289 fprintf(out, " return success? 0 : -1;\n");
290
291 } else if (minApiLevel == API_Q) {
292 int argIndex = 1;
293 fprintf(out, " StatsEventCompat event;\n");
294 fprintf(out, " event.setAtomId(code);\n");
295 write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "event.", "",
296 minApiLevel);
297 for (vector<java_type_t>::const_iterator arg = signature.begin();
298 arg != signature.end(); arg++) {
299 switch (*arg) {
300 case JAVA_TYPE_ATTRIBUTION_CHAIN: {
301 const char* uidName = attributionDecl.fields.front().name.c_str();
302 const char* tagName = attributionDecl.fields.back().name.c_str();
303 fprintf(out, " event.writeAttributionChain(%s, %s_length, %s);\n",
304 uidName, uidName, tagName);
305 break;
306 }
307 case JAVA_TYPE_BYTE_ARRAY:
308 fprintf(out, " event.writeByteArray(arg%d.arg, arg%d.arg_length);\n",
309 argIndex, argIndex);
310 break;
311 case JAVA_TYPE_BOOLEAN:
312 fprintf(out, " event.writeBool(arg%d);\n", argIndex);
313 break;
314 case JAVA_TYPE_INT: // Fall through.
315 case JAVA_TYPE_ENUM:
316 fprintf(out, " event.writeInt32(arg%d);\n", argIndex);
317 break;
318 case JAVA_TYPE_FLOAT:
319 fprintf(out, " event.writeFloat(arg%d);\n", argIndex);
320 break;
321 case JAVA_TYPE_LONG:
322 fprintf(out, " event.writeInt64(arg%d);\n", argIndex);
323 break;
324 case JAVA_TYPE_STRING:
325 fprintf(out, " event.writeString(arg%d);\n", argIndex);
326 break;
327 default:
328 // Unsupported types: OBJECT, DOUBLE, and all repeated
329 // fields.
330 fprintf(stderr, "Encountered unsupported type.\n");
331 return 1;
332 }
333 write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "event.", "",
334 minApiLevel);
335 argIndex++;
336 }
337 fprintf(out, " return event.writeToSocket();\n"); // end method body.
338 } else {
339 fprintf(out, " AStatsEvent* event = AStatsEvent_obtain();\n");
340 const int ret = write_native_method_body(out, signature, fieldNumberToAtomDeclSet,
341 attributionDecl, minApiLevel);
342 if (ret != 0) {
343 return ret;
344 }
345 fprintf(out, " const int ret = AStatsEvent_write(event);\n");
346 fprintf(out, " AStatsEvent_release(event);\n");
347 fprintf(out, " return ret;\n"); // end method body.
348 }
349 fprintf(out, "}\n\n"); // end method.
350 }
351 return 0;
352 }
353
write_native_stats_write_non_chained_methods(FILE * out,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl)354 static void write_native_stats_write_non_chained_methods(FILE* out,
355 const SignatureInfoMap& signatureInfoMap,
356 const AtomDecl& attributionDecl) {
357 fprintf(out, "\n");
358 for (const auto& [signature, _] : signatureInfoMap) {
359 write_native_method_signature(out, "int stats_write_non_chained(", signature,
360 attributionDecl, " {");
361
362 vector<java_type_t> newSignature;
363
364 // First two args form the attribution node so size goes down by 1.
365 newSignature.reserve(signature.size() - 1);
366
367 // First arg is Attribution Chain.
368 newSignature.push_back(JAVA_TYPE_ATTRIBUTION_CHAIN);
369
370 // Followed by the originial signature except the first 2 args.
371 newSignature.insert(newSignature.end(), signature.begin() + 2, signature.end());
372
373 const char* uidName = attributionDecl.fields.front().name.c_str();
374 const char* tagName = attributionDecl.fields.back().name.c_str();
375 fprintf(out, " const int32_t* %s = &arg1;\n", uidName);
376 fprintf(out, " const size_t %s_length = 1;\n", uidName);
377 fprintf(out, " const std::vector<char const*> %s(1, arg2);\n", tagName);
378 fprintf(out, " return ");
379 write_native_method_call(out, "stats_write", newSignature, attributionDecl, 2);
380
381 fprintf(out, "}\n\n");
382 }
383 }
384
write_native_build_stats_event_methods(FILE * out,const SignatureInfoMap & signatureInfoMap,const AtomDecl & attributionDecl,const int minApiLevel)385 static int write_native_build_stats_event_methods(FILE* out,
386 const SignatureInfoMap& signatureInfoMap,
387 const AtomDecl& attributionDecl,
388 const int minApiLevel) {
389 fprintf(out, "\n");
390 for (const auto& [signature, fieldNumberToAtomDeclSet] : signatureInfoMap) {
391 write_native_method_signature(out, "void addAStatsEvent(AStatsEventList* pulled_data, ",
392 signature, attributionDecl, " {");
393
394 fprintf(out, " AStatsEvent* event = AStatsEventList_addStatsEvent(pulled_data);\n");
395 const int ret = write_native_method_body(out, signature, fieldNumberToAtomDeclSet,
396 attributionDecl, minApiLevel);
397 if (ret != 0) {
398 return ret;
399 }
400 fprintf(out, " AStatsEvent_build(event);\n"); // end method body.
401
402 fprintf(out, "}\n\n"); // end method.
403 }
404 return 0;
405 }
406
write_stats_log_cpp(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const string & cppNamespace,const string & importHeader,const int minApiLevel,bool bootstrap)407 int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
408 const string& cppNamespace, const string& importHeader,
409 const int minApiLevel, bool bootstrap) {
410 // Print prelude
411 fprintf(out, "// This file is autogenerated\n");
412 fprintf(out, "\n");
413
414 fprintf(out, "#include <%s>\n", importHeader.c_str());
415 if (!bootstrap) {
416 if (minApiLevel == API_Q) {
417 fprintf(out, "#include <StatsEventCompat.h>\n");
418 } else {
419 fprintf(out, "#include <stats_event.h>\n");
420 }
421
422 if (minApiLevel > API_R) {
423 fprintf(out, "#include <stats_annotations.h>\n");
424 }
425
426 if (minApiLevel > API_Q && !atoms.pulledAtomsSignatureInfoMap.empty()) {
427 fprintf(out, "#include <stats_pull_atom_callback.h>\n");
428 }
429 } else {
430 fprintf(out, "#include <StatsBootstrapAtomClient.h>\n");
431 fprintf(out, "#include <android/os/StatsBootstrapAtom.h>\n");
432 fprintf(out, "#include <utils/String16.h>\n");
433 }
434
435 fprintf(out, "\n");
436 write_namespace(out, cppNamespace);
437
438 int ret = write_native_stats_write_methods(out, atoms.signatureInfoMap, attributionDecl,
439 minApiLevel, bootstrap);
440 if (ret != 0) {
441 return ret;
442 }
443 if (!bootstrap) {
444 write_native_stats_write_non_chained_methods(out, atoms.nonChainedSignatureInfoMap,
445 attributionDecl);
446 ret = write_native_build_stats_event_methods(out, atoms.pulledAtomsSignatureInfoMap,
447 attributionDecl, minApiLevel);
448 if (ret != 0) {
449 return ret;
450 }
451 }
452
453 // Print footer
454 fprintf(out, "\n");
455 write_closing_namespace(out, cppNamespace);
456
457 return 0;
458 }
459
write_stats_log_header(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl,const string & cppNamespace,const int minApiLevel,bool bootstrap)460 int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
461 const string& cppNamespace, const int minApiLevel, bool bootstrap) {
462 const bool includePull = !atoms.pulledAtomsSignatureInfoMap.empty() && !bootstrap;
463 write_native_header_preamble(out, cppNamespace, includePull);
464 write_native_atom_constants(out, atoms, attributionDecl);
465 write_native_atom_enums(out, atoms);
466
467 if (minApiLevel <= API_R) {
468 write_native_annotation_constants(out);
469 }
470
471 fprintf(out, "struct BytesField {\n");
472 fprintf(out,
473 " BytesField(char const* array, size_t len) : arg(array), "
474 "arg_length(len) {}\n");
475 fprintf(out, " char const* arg;\n");
476 fprintf(out, " size_t arg_length;\n");
477 fprintf(out, "};\n");
478 fprintf(out, "\n");
479
480 // Print write methods
481 fprintf(out, "//\n");
482 fprintf(out, "// Write methods\n");
483 fprintf(out, "//\n");
484 write_native_method_header(out, "int stats_write(", atoms.signatureInfoMap, attributionDecl);
485 fprintf(out, "\n");
486
487 // Attribution chains and pulled atoms are not supported for bootstrap processes.
488 if (!bootstrap) {
489 fprintf(out, "//\n");
490 fprintf(out, "// Write flattened methods\n");
491 fprintf(out, "//\n");
492 write_native_method_header(out, "int stats_write_non_chained(",
493 atoms.nonChainedSignatureInfoMap, attributionDecl);
494 fprintf(out, "\n");
495
496 // Print pulled atoms methods.
497 fprintf(out, "//\n");
498 fprintf(out, "// Add AStatsEvent methods\n");
499 fprintf(out, "//\n");
500 write_native_method_header(out, "void addAStatsEvent(AStatsEventList* pulled_data, ",
501 atoms.pulledAtomsSignatureInfoMap, attributionDecl);
502 fprintf(out, "\n");
503 }
504
505 write_native_header_epilogue(out, cppNamespace);
506
507 return 0;
508 }
509
510 } // namespace stats_log_api_gen
511 } // namespace android
512